You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, aberrationAmount = 3, pinkTint = 0.3, cyanTint = 0.2, scanlineIntensity = 0.2, scanlineHeight = 2, scanlineGap = 2) {
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can optimize repeated getImageData/putImageData calls
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const originalWidth = originalImg.naturalWidth;
const originalHeight = originalImg.naturalHeight;
// Handle cases where image might not be loaded or is invalid
if (originalWidth === 0 || originalHeight === 0) {
canvas.width = 1; // Avoid 0x0 canvas
canvas.height = 1;
console.warn("Image Vaporwave Effect Filter: Original image has zero width or height. Ensure the image is loaded before processing.");
return canvas;
}
canvas.width = originalWidth;
canvas.height = originalHeight;
// --- 1. Draw original image ---
ctx.drawImage(originalImg, 0, 0, originalWidth, originalHeight);
// --- 2. Chromatic Aberration ---
// Ensure aberrationAmount is an integer for pixel offsets
const caAmount = Math.floor(aberrationAmount);
if (caAmount !== 0) { // Apply if caAmount is non-zero (positive or negative)
const srcData = ctx.getImageData(0, 0, originalWidth, originalHeight);
const dstData = ctx.createImageData(originalWidth, originalHeight); // Create new ImageData for the output
const srcPixels = srcData.data;
const dstPixels = dstData.data;
for (let y = 0; y < originalHeight; y++) {
for (let x = 0; x < originalWidth; x++) {
const i = (y * originalWidth + x) * 4;
// Red channel from x - caAmount (clamped to image bounds)
const rX = Math.max(0, Math.min(originalWidth - 1, x - caAmount));
const rIndex = (y * originalWidth + rX) * 4;
// Green channel from original x (no shift)
const gIndex = i;
// Blue channel from x + caAmount (clamped to image bounds)
const bX = Math.max(0, Math.min(originalWidth - 1, x + caAmount));
const bIndex = (y * originalWidth + bX) * 4;
dstPixels[i] = srcPixels[rIndex]; // Red
dstPixels[i + 1] = srcPixels[gIndex + 1]; // Green
dstPixels[i + 2] = srcPixels[bIndex + 2]; // Blue
dstPixels[i + 3] = srcPixels[gIndex + 3]; // Alpha
}
}
ctx.putImageData(dstData, 0, 0); // Apply aberration to canvas
}
// --- 3. Get image data (potentially with CA applied) for color tinting and scanlines ---
let imageData = ctx.getImageData(0, 0, originalWidth, originalHeight);
let data = imageData.data;
// Ensure scanline parameters are reasonable
const sHeight = Math.max(0, scanlineHeight); // Allow 0 for no scanlines
const sGap = Math.max(0, scanlineGap);
const sCycle = sHeight + sGap; // Total height of one scanline + gap cycle
// Clamp scanlineIntensity between 0 and 1
const clampedScanlineIntensity = Math.max(0, Math.min(1, scanlineIntensity));
// --- 4. Color Tinting & Scanlines (Combined loop for efficiency) ---
for (let y = 0; y < originalHeight; y++) {
// Determine if the current row is part of a scanline
const isScanlineRow = (clampedScanlineIntensity > 0 && sHeight > 0 && sCycle > 0) && ((y % sCycle) < sHeight);
for (let x = 0; x < originalWidth; x++) {
const i = (y * originalWidth + x) * 4;
let r = data[i];
let g = data[i+1];
let b = data[i+2];
// Vaporwave Color Tinting:
// These factors are heuristic, aiming for a pink/magenta and cyan/blue shift,
// while typically reducing the green channel.
let newR = r * (1 + pinkTint * 0.5) + b * (pinkTint * 0.3);
let newG = g * (1 - (pinkTint + cyanTint) * 0.6); // Green is often de-emphasized
let newB = b * (1 + cyanTint * 0.5) + r * (cyanTint * 0.3);
// Clamp color values to the valid 0-255 range
data[i] = Math.min(255, Math.max(0, newR));
data[i+1] = Math.min(255, Math.max(0, newG));
data[i+2] = Math.min(255, Math.max(0, newB));
// Apply Scanlines on the (now tinted) pixel
if (isScanlineRow) {
const factor = (1 - clampedScanlineIntensity); // Darken pixels for scanline effect
data[i] = Math.max(0, data[i] * factor);
data[i+1] = Math.max(0, data[i+1] * factor);
data[i+2] = Math.max(0, data[i+2] * factor);
}
}
}
// --- 5. Put modified data back to the canvas ---
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes