You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, bassLoudness = "50", enableBassShake = "true", drawEqualizer = "true") {
// Parse arguments and clamp to sensible ranges
const loudnessStr = String(bassLoudness);
const loudnessFloat = Math.max(0, Math.min(100, parseFloat(loudnessStr) || 50)) / 100;
const isBassShake = String(enableBassShake).toLowerCase() === "true" || String(enableBassShake) === "1";
const isDrawEq = String(drawEqualizer).toLowerCase() === "true" || String(drawEqualizer) === "1";
const canvas = document.createElement('canvas');
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
// Ensure we have a valid dimension
if (width === 0 || height === 0) return canvas;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Initial draw to capture image data
ctx.drawImage(originalImg, 0, 0, width, height);
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
const outData = new Uint8ClampedArray(data.length);
// Max horizontal displacement for vibration effect
const maxOffset = Math.floor(width * 0.08 * loudnessFloat);
// 1. Apply Chromatic Aberration, Bass Shaking (Vibration), and Deep Fried Colors
for (let y = 0; y < height; y++) {
// Low Frequency Wave (Low Voice bass vibration)
let wave = 0;
if (isBassShake) {
// Broad wave periods represent low frequency voice/bass
wave = Math.sin(y * 0.02) * maxOffset;
}
// High frequency micro-jitter (distortion)
const jitter = (Math.random() - 0.5) * maxOffset * 0.2;
const offset = isBassShake ? wave + jitter : 0;
for (let x = 0; x < width; x++) {
const srcIdx = (y * width + x) * 4;
outData[srcIdx + 3] = data[srcIdx + 3]; // Preserve alpha
let rX = x; let gX = x; let bX = x;
if (isBassShake) {
// Chromatic Aberration splits RGB channels slightly laterally
const rOffset = Math.floor(offset + (maxOffset * 0.4));
const gOffset = Math.floor(offset);
const bOffset = Math.floor(offset - (maxOffset * 0.4));
rX = Math.min(width - 1, Math.max(0, x + rOffset));
gX = Math.min(width - 1, Math.max(0, x + gOffset));
bX = Math.min(width - 1, Math.max(0, x + bOffset));
}
outData[srcIdx] = data[(y * width + rX) * 4]; // R
outData[srcIdx + 1] = data[(y * width + gX) * 4 + 1]; // G
outData[srcIdx + 2] = data[(y * width + bX) * 4 + 2]; // B
}
}
// High contrast factor (up to max 120 offset)
const contrast = loudnessFloat * 120;
const cFactor = (259 * (contrast + 255)) / (255 * (259 - contrast));
for (let i = 0; i < outData.length; i += 4) {
if (outData[i + 3] === 0) continue; // Skip fully transparent pixels
let r = outData[i];
let g = outData[i + 1];
let b = outData[i + 2];
if (loudnessFloat > 0) {
// Deep fried aesthetic: intense saturation & strong red presence
const gray = 0.2989 * r + 0.5870 * g + 0.1140 * b;
const satFactor = 1 + loudnessFloat * 2.5; // up to 3.5x saturation
r = gray + satFactor * (r - gray);
g = gray + satFactor * (g - gray);
b = gray + satFactor * (b - gray);
// "Bass boosted" reddish thermal shift
r += 50 * loudnessFloat;
g += 10 * loudnessFloat;
b -= 30 * loudnessFloat;
}
// Apply contrast
r = cFactor * (r - 128) + 128;
g = cFactor * (g - 128) + 128;
b = cFactor * (b - 128) + 128;
// Uint8ClampedArray handles clamping to 0-255 intrinsically
outData[i] = r;
outData[i + 1] = g;
outData[i + 2] = b;
}
// Output manipulated pixels back to canvas
ctx.putImageData(new ImageData(outData, width, height), 0, 0);
// 2. Pixelation blockiness / Low quality JPEG artifacting
if (loudnessFloat > 0.1) {
const downscaleFactor = Math.max(0.1, 1 - (loudnessFloat * 0.8)); // size down to 20%
const tempCanvas = document.createElement('canvas');
const tWidth = Math.floor(width * downscaleFactor);
const tHeight = Math.floor(height * downscaleFactor);
tempCanvas.width = tWidth > 0 ? tWidth : 1;
tempCanvas.height = tHeight > 0 ? tHeight : 1;
const tCtx = tempCanvas.getContext('2d');
tCtx.drawImage(canvas, 0, 0, tempCanvas.width, tempCanvas.height);
// Redraw upscaled without smoothing for the blocky pixelation effect
ctx.imageSmoothingEnabled = false;
ctx.drawImage(tempCanvas, 0, 0, tempCanvas.width, tempCanvas.height, 0, 0, width, height);
ctx.imageSmoothingEnabled = true;
// Dark heavy vignette for "Low Key" atmospheric focus
const grad = ctx.createRadialGradient(width / 2, height / 2, Math.min(width, height) * 0.3, width / 2, height / 2, Math.max(width, height) * 0.8);
grad.addColorStop(0, "rgba(0,0,0,0)");
grad.addColorStop(1, `rgba(0,0,0,${loudnessFloat * 0.85})`);
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = grad;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = "source-over"; // reset
}
// 3. Draw Low Audio Frequency Equalizer overlay
if (isDrawEq && loudnessFloat > 0) {
ctx.globalCompositeOperation = "screen";
ctx.fillStyle = `rgba(255, 60, 60, ${0.4 + loudnessFloat * 0.6})`;
const numBars = 40;
const barWidth = width / numBars;
const maxBarHeight = height * 0.25 * loudnessFloat; // Eq takes up to 25% of image bottom
for (let b = 0; b < numBars; b++) {
// Emphasis on the left to symbolize Low Frequencies (Sub/Bass)
const lowFreqEmphasis = 1 - Math.pow(b / numBars, 2);
// Creates a faux audio sine form
const baseShape = Math.abs(Math.sin((b / numBars) * Math.PI * 1.5)) * 0.6 + 0.4;
const amp = lowFreqEmphasis * baseShape * (0.5 + Math.random() * 0.5);
const barH = amp * maxBarHeight;
const x = b * barWidth;
ctx.fillRect(x + barWidth * 0.1, height - barH, barWidth * 0.8, barH);
}
ctx.globalCompositeOperation = "source-over"; // reset
}
return canvas;
}
Apply Changes