You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, glitchIntensity = "50", waveformAmplitude = "30", channelShift = "15") {
// Parse arguments and restrict values
const intensity = Math.max(0, Math.min(100, Number(glitchIntensity)));
const amp = Number(waveformAmplitude);
const shiftX = Number(channelShift);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
// Draw original image to extract pixel data
ctx.drawImage(originalImg, 0, 0);
const imgData = ctx.getImageData(0, 0, w, h);
const data = imgData.data;
// Copy original to outData so unwritten/skipped pixels retain original image data seamlessly
const outData = new Uint8ClampedArray(data);
// 1. Generate a simulated "Audio Signal" based on the image's own luminance
const audioSignal = new Float32Array(h);
for (let y = 0; y < h; y++) {
let rowLum = 0;
for (let x = 0; x < w; x++) {
const idx = (y * w + x) * 4;
rowLum += 0.299 * data[idx] + 0.587 * data[idx+1] + 0.114 * data[idx+2];
}
// Normalize between -1 and 1
audioSignal[y] = ((rowLum / w) / 255) * 2 - 1;
// Add random high-frequency noise scaled by glitch intensity
if (intensity > 0) {
audioSignal[y] += (Math.random() - 0.5) * (intensity / 50);
}
}
// 2. Apply Glitch / Audio Databending Effects to image pixels
for (let y = 0; y < h; y++) {
// Pixel rows are horizontally displaced by the simulated audio signal
const displacement = Math.round(audioSignal[y] * amp * (intensity / 25));
for (let x = 0; x < w; x++) {
const srcIdx = (y * w + x) * 4;
// Calculate horizontally wrapped destinations
const destX = ((x + displacement) % w + w) % w;
const destIdx = (y * w + destX) * 4;
// Stereo delay channel shifting (Chromatic Aberration)
const gX = ((destX + shiftX) % w + w) % w;
const gIdx = (y * w + gX) * 4;
const bX = ((destX - shiftX) % w + w) % w;
const bIdx = (y * w + bX) * 4;
// Write distorted pixels
outData[destIdx] = data[srcIdx]; // Red channel
outData[gIdx + 1] = data[srcIdx + 1]; // Green channel
outData[bIdx + 2] = data[srcIdx + 2]; // Blue channel
outData[destIdx + 3] = data[srcIdx + 3]; // Alpha channel
}
}
// 3. Simulate audio buffer underrun / CD skipping (Block Tears)
const numBlocks = Math.floor((intensity / 100) * 30);
for (let i = 0; i < numBlocks; i++) {
const blockY = Math.floor(Math.random() * h);
const blockH = Math.floor(Math.random() * (h / 10) + 2);
const shift = Math.floor((Math.random() - 0.5) * w * 0.8);
for (let by = blockY; by < Math.min(h, blockY + blockH); by++) {
const rowData = new Uint8ClampedArray(w * 4);
// Shift the entire row
for (let x = 0; x < w; x++) {
const srcX = ((x - shift) % w + w) % w;
const srcI = (by * w + srcX) * 4;
const dstI = x * 4;
rowData[dstI] = outData[srcI];
rowData[dstI+1] = outData[srcI+1];
rowData[dstI+2] = outData[srcI+2];
rowData[dstI+3] = outData[srcI+3];
}
// Assign the shifted row back
for (let x = 0; x < w; x++) {
const i = (by * w + x) * 4;
outData[i] = rowData[x * 4];
outData[i+1] = rowData[x * 4 + 1];
outData[i+2] = rowData[x * 4 + 2];
outData[i+3] = rowData[x * 4 + 3];
}
}
}
imgData.data.set(outData);
ctx.putImageData(imgData, 0, 0);
// 4. GUI Overlay: Visual Audio "Loader" Graphic Design
const overlayH = Math.min(120, h * 0.25);
const overlayY = h - overlayH;
// Background for terminal logic
ctx.fillStyle = 'rgba(0, 0, 0, 0.75)';
ctx.fillRect(0, overlayY, w, overlayH);
// Draw Audio Waveform based directly on the image's generated audio signal
ctx.strokeStyle = '#00FF41'; // Terminal green
ctx.lineWidth = Math.max(1, w * 0.003);
ctx.beginPath();
for (let x = 0; x < w; x++) {
// Map graphical X coordinate back to signal index
const sigIdx = Math.floor((x / w) * h);
let val = audioSignal[sigIdx] * (overlayH * 0.25);
// Sputter / glitch the waveform itself
if (Math.random() * 100 < intensity) {
val += (Math.random() - 0.5) * (overlayH * 0.2);
}
const yPos = overlayY + (overlayH * 0.45) + val;
if (x === 0) ctx.moveTo(x, yPos);
else ctx.lineTo(x, yPos);
}
ctx.stroke();
// Glitch Text
let fontSize = Math.max(10, overlayH * 0.15);
ctx.font = `bold ${fontSize}px monospace`;
ctx.fillStyle = '#00FF41';
let texts = [
"AUDIO DATA_BEND // CLIP LOADED //",
"AUD** D%TA_BEND // C#I* ##ERR0R//",
"S Y N C L O S T",
"F I L E C O R R U P T E D",
"B U F F E R U N D E R R U N"
];
let loadText = texts[0];
if (Math.random() < (intensity / 100)) {
loadText = texts[Math.floor(Math.random() * (texts.length - 1)) + 1];
}
ctx.fillText(loadText, w * 0.02, overlayY + fontSize * 1.5);
// Render a Fake Audio Progress Bar
const barY = h - (overlayH * 0.2);
const barH = overlayH * 0.1;
ctx.fillStyle = 'rgba(0, 255, 65, 0.25)';
ctx.fillRect(w * 0.02, barY, w * 0.96, barH);
ctx.fillStyle = '#00FF41';
const progress = Math.min(100, Math.random() * 80 + 20); // Simulates loading halt point
ctx.fillRect(w * 0.02, barY, (w * 0.96) * (progress / 100), barH);
// 5. Apply subtle global TV scanlines over everything to complete aesthetic
if (intensity > 15) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
for (let y = 0; y < h; y += 4) {
ctx.fillRect(0, y, w, 2);
}
}
return canvas;
}
Apply Changes