Please bookmark this page to avoid losing your image tool!

Image Retrowave Filter Application

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(
    originalImg,
    shadowColor = "rgb(40, 10, 70)",    // Dark Purple-Blue
    highlightColor = "rgb(255, 80, 150)", // Vibrant Pink
    contrastValue = 1.4,
    brightnessValue = 5,
    scanlineOpacity = 0.15,
    scanlineThickness = 1,
    scanlineGap = 2
) {

    // Helper function to parse color strings (e.g., "rgb(r,g,b)", "#RRGGBB", "colorname")
    // to an {r, g, b} object.
    function parseColor(colorStr) {
        // Create a temporary 1x1 canvas to draw the color and get its RGB components.
        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = 1;
        tempCanvas.height = 1;
        const tempCtx = tempCanvas.getContext('2d');
        
        if (!tempCtx) { // Fallback for safety, though highly unlikely in browsers
            return { r: 0, g: 0, b: 0 }; 
        }

        tempCtx.fillStyle = colorStr;
        tempCtx.fillRect(0, 0, 1, 1);
        const pixelData = tempCtx.getImageData(0, 0, 1, 1).data;
        return { r: pixelData[0], g: pixelData[1], b: pixelData[2] };
    }

    // Clamp utility function
    function clamp(value, min, max) {
        return Math.max(min, Math.min(max, value));
    }

    const canvas = document.createElement('canvas');
    
    // Use naturalWidth/Height if available, otherwise fallback to width/height.
    // Provide default dimensions if image info is missing.
    const imgWidth = originalImg.naturalWidth || originalImg.width || 300;
    const imgHeight = originalImg.naturalHeight || originalImg.height || 150;

    canvas.width = imgWidth;
    canvas.height = imgHeight;

    const ctx = canvas.getContext('2d');

    if (!ctx) {
        console.error("Failed to get 2D context for the main canvas.");
        // Return the canvas element, even if unusable, as per preference.
        // The caller might handle this or it might appear as a broken element.
        return canvas;
    }

    // Draw the original image onto the canvas
    try {
        ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    } catch (e) {
        console.error("Error drawing image: ", e);
        ctx.fillStyle = "rgba(255, 0, 0, 0.7)";
        ctx.fillRect(0,0, canvas.width, canvas.height);
        ctx.font = "16px Arial";
        ctx.fillStyle = "white";
        ctx.textAlign = "center";
        ctx.fillText("Error: Could not draw image.", canvas.width / 2, canvas.height / 2);
        return canvas;
    }
    

    // Get image data for pixel manipulation
    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        console.error("Could not getImageData (e.g., CORS issue if image is cross-origin): ", e);
        // Draw an error message on the canvas
        ctx.fillStyle = "rgba(0,0,0,0.7)"; // Semi-transparent overlay
        ctx.fillRect(0,0, canvas.width, canvas.height);
        ctx.font = "16px Arial";
        ctx.fillStyle = "red";
        ctx.textAlign = "center";
        ctx.fillText("Error: Cannot process image pixels (CORS issue?).", canvas.width / 2, canvas.height / 2);
        return canvas;
    }
    
    const data = imageData.data;
    const sColorRGB = parseColor(shadowColor);
    const hColorRGB = parseColor(highlightColor);

    // Pixel manipulation loop
    for (let i = 0; i < data.length; i += 4) {
        let rOrig = data[i];
        let gOrig = data[i + 1];
        let bOrig = data[i + 2];

        // 1. Apply Contrast
        // The midpoint 127.5 is used for contrast calculation.
        let r = (rOrig - 127.5) * contrastValue + 127.5;
        let g = (gOrig - 127.5) * contrastValue + 127.5;
        let b = (bOrig - 127.5) * contrastValue + 127.5;
        
        // 2. Apply Brightness
        r += brightnessValue;
        g += brightnessValue;
        b += brightnessValue;

        // Clamp values after contrast and brightness adjustments
        r = clamp(r, 0, 255);
        g = clamp(g, 0, 255);
        b = clamp(b, 0, 255);
        
        // 3. Duotone mapping based on luminance of the adjusted color
        const gray = 0.299 * r + 0.587 * g + 0.114 * b;
        const lumNorm = gray / 255; // Normalized luminance (0-1)

        // Interpolate between shadow and highlight colors based on luminance
        data[i]   = clamp(sColorRGB.r * (1 - lumNorm) + hColorRGB.r * lumNorm, 0, 255);
        data[i+1] = clamp(sColorRGB.g * (1 - lumNorm) + hColorRGB.g * lumNorm, 0, 255);
        data[i+2] = clamp(sColorRGB.b * (1 - lumNorm) + hColorRGB.b * lumNorm, 0, 255);
        // Alpha channel (data[i+3]) remains unchanged
    }

    // Put the modified image data back onto the canvas
    ctx.putImageData(imageData, 0, 0);

    // 4. Draw Scanlines
    const st = Math.max(0, scanlineThickness); // Ensure thickness is not negative
    const so = clamp(scanlineOpacity, 0, 1);   // Ensure opacity is between 0 and 1
    
    if (st > 0 && so > 0) {
        ctx.fillStyle = `rgba(0, 0, 0, ${so})`;
        const sg = Math.max(0, scanlineGap); // Ensure gap is not negative
        const step = st + sg;
        if (step > 0) { // Ensure we make progress in the loop
            for (let y = 0; y < canvas.height; y += step) {
                ctx.fillRect(0, y, canvas.width, st);
            }
        }
    }

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Retrowave Filter Application allows users to transform their images with a retro aesthetic by applying a unique combination of color adjustments, contrast and brightness enhancements, and scanline effects. This tool is ideal for artists, designers, and social media enthusiasts looking to create visually striking images reminiscent of 80s style. Users can customize shadow and highlight colors, as well as adjust contrast, brightness, and scanline attributes to achieve their desired retro look.

Leave a Reply

Your email address will not be published. Required fields are marked *