Please bookmark this page to avoid losing your image tool!

Image Theater Maker

(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, spotlightIntensity = "0.2") {
    const w_img = originalImg.width;
    const h_img = originalImg.height;

    // We pad the canvas proportionally so the original image isn't cropped 
    // and correctly looks like a backdrop screen on a stage.
    const padX = Math.round(w_img * (0.2 / 0.6));
    const padYTop = Math.round(h_img * (0.225 / 0.625));
    const padYBottom = Math.round(h_img * (0.15 / 0.625));

    const w = padX + w_img + padX;
    const h = padYTop + h_img + padYBottom;

    const w_c = padX;
    const floorY = padYTop + h_img;
    const h_v = Math.round(padYTop / 1.5);

    const screenX = padX;
    const screenY = padYTop;

    const canvas = document.createElement('canvas');
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext('2d');

    // 1. Back Wall (Dark brick texture)
    ctx.fillStyle = '#0f1112';
    ctx.fillRect(0, 0, w, floorY);

    ctx.strokeStyle = '#050505';
    ctx.lineWidth = Math.max(1, w * 0.002);
    let bWidth = Math.max(20, w * 0.04);
    let bHeight = Math.max(10, w * 0.015);

    for (let j = 0; j <= floorY; j += bHeight) {
        ctx.beginPath();
        ctx.moveTo(0, j);
        ctx.lineTo(w, j);
        ctx.stroke();
    }
    for (let j = 0; j <= floorY; j += bHeight) {
        let offset = (Math.round(j / bHeight) % 2 === 0) ? 0 : bWidth / 2;
        ctx.beginPath();
        for (let i = offset; i <= w; i += bWidth) {
            ctx.moveTo(i, j);
            ctx.lineTo(i, j + bHeight);
        }
        ctx.stroke();
    }

    // 2. Stage Floor (Wood perspective)
    let floorGrad = ctx.createLinearGradient(0, floorY, 0, h);
    floorGrad.addColorStop(0, '#0a0500');
    floorGrad.addColorStop(1, '#3b2313');
    ctx.fillStyle = floorGrad;
    ctx.fillRect(0, floorY, w, h - floorY);

    ctx.strokeStyle = 'rgba(0, 0, 0, 0.6)';
    ctx.lineWidth = Math.max(2, w * 0.002);
    let vpX = w / 2;
    let vpY = h * 0.4;
    for (let i = -20; i <= 20; i++) {
        let targetX = vpX + i * (w * 0.05);
        let startX = vpX + (targetX - vpX) * ((floorY - vpY) / (h - vpY));
        ctx.beginPath();
        ctx.moveTo(startX, floorY);
        ctx.lineTo(targetX, h);
        ctx.stroke();
    }

    let currentY = floorY;
    while (currentY < h) {
        ctx.beginPath();
        ctx.moveTo(0, currentY);
        ctx.lineTo(w, currentY);
        ctx.stroke();
        let step = (currentY - vpY) * 0.08;
        currentY += Math.max(1, step);
    }

    // 3. Movie Screen Frame
    ctx.fillStyle = '#444';
    let frameOuter = Math.max(2, w * 0.01);
    ctx.fillRect(screenX - frameOuter, screenY - frameOuter, w_img + frameOuter * 2, h_img + frameOuter * 2);

    ctx.fillStyle = '#222';
    let frameInner = Math.max(1, w * 0.005);
    ctx.fillRect(screenX - frameInner, screenY - frameInner, w_img + frameInner * 2, h_img + frameInner * 2);

    // 4. Original Image (Projected Screen)
    ctx.drawImage(originalImg, screenX, screenY, w_img, h_img);

    // 5. Footlights
    ctx.beginPath();
    ctx.moveTo(0, floorY);
    ctx.lineTo(w, floorY);
    ctx.strokeStyle = '#050505';
    ctx.lineWidth = Math.max(3, w * 0.005);
    ctx.stroke();

    let nl = 15;
    for (let i = 1; i <= nl; i++) {
        let lx = i * (w / (nl + 1));
        let ly = floorY;

        ctx.beginPath();
        ctx.arc(lx, ly, Math.max(2, w * 0.015), Math.PI, 0);
        ctx.fillStyle = '#111';
        ctx.fill();

        ctx.beginPath();
        ctx.arc(lx, ly, Math.max(1, w * 0.005), Math.PI, 0);
        ctx.fillStyle = '#ffffe0';
        ctx.fill();

        let glow = ctx.createRadialGradient(lx, ly, 0, lx, ly, w * 0.04);
        glow.addColorStop(0, 'rgba(255, 255, 200, 0.4)');
        glow.addColorStop(1, 'rgba(255, 255, 200, 0)');
        ctx.fillStyle = glow;
        ctx.beginPath();
        ctx.arc(lx, ly, w * 0.04, Math.PI, 0);
        ctx.fill();
    }

    // 6. Spotlights
    let intensity = Math.min(1, Math.max(0, parseFloat(spotlightIntensity) || 0.2));
    if (intensity > 0) {
        ctx.globalCompositeOperation = 'screen';
        let spotStartL = `rgba(255, 255, 240, ${intensity})`;
        let spotEnd = 'rgba(255, 255, 240, 0)';

        let spotL = ctx.createLinearGradient(w * 0.05, 0, w * 0.5, h * 0.8);
        spotL.addColorStop(0, spotStartL);
        spotL.addColorStop(1, spotEnd);
        ctx.fillStyle = spotL;
        ctx.beginPath();
        ctx.moveTo(w * 0.05, 0);
        ctx.lineTo(w * 0.3, h * 0.8);
        ctx.lineTo(w * 0.7, h * 0.8);
        ctx.closePath();
        ctx.fill();

        let spotR = ctx.createLinearGradient(w * 0.95, 0, w * 0.5, h * 0.8);
        spotR.addColorStop(0, spotStartL);
        spotR.addColorStop(1, spotEnd);
        ctx.fillStyle = spotR;
        ctx.beginPath();
        ctx.moveTo(w * 0.95, 0);
        ctx.lineTo(w * 0.7, h * 0.8);
        ctx.lineTo(w * 0.3, h * 0.8);
        ctx.closePath();
        ctx.fill();
        ctx.globalCompositeOperation = 'source-over';
    }

    // 7. Left Curtain + Right Curtain
    let folds = 7;

    // Left Curtain Setup
    ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
    ctx.shadowBlur = Math.max(10, w * 0.015);
    ctx.shadowOffsetX = Math.max(3, w * 0.005);
    ctx.shadowOffsetY = Math.max(3, h * 0.005);

    let leftGrad = ctx.createLinearGradient(0, 0, w_c, 0);
    for (let i = 0; i <= folds * 2; i++) {
        leftGrad.addColorStop(i / (folds * 2), i % 2 === 0 ? '#2b0000' : '#bc0000');
    }
    ctx.fillStyle = leftGrad;
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(w_c, 0);
    ctx.quadraticCurveTo(w_c, h * 0.3, w_c * 0.3, h * 0.6);
    ctx.quadraticCurveTo(w_c * 0.3, h * 0.8, w_c * 0.7, h);
    ctx.lineTo(0, h);
    ctx.closePath();
    ctx.fill();

    ctx.beginPath();
    ctx.moveTo(w_c, 0);
    ctx.quadraticCurveTo(w_c, h * 0.3, w_c * 0.3, h * 0.6);
    ctx.quadraticCurveTo(w_c * 0.3, h * 0.8, w_c * 0.7, h);
    ctx.strokeStyle = '#DAA520';
    ctx.lineWidth = Math.max(2, h * 0.008);
    ctx.lineCap = 'round';
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(0, h * 0.58);
    ctx.lineTo(w_c * 0.35, h * 0.6);
    ctx.lineTo(0, h * 0.62);
    ctx.fillStyle = '#DAA520';
    ctx.fill();

    // Right Curtain Setup
    ctx.shadowOffsetX = -Math.max(3, w * 0.005);

    let rightGrad = ctx.createLinearGradient(w - w_c, 0, w, 0);
    for (let i = 0; i <= folds * 2; i++) {
        rightGrad.addColorStop(i / (folds * 2), i % 2 === 0 ? '#2b0000' : '#bc0000');
    }
    ctx.fillStyle = rightGrad;
    ctx.beginPath();
    ctx.moveTo(w, 0);
    ctx.lineTo(w - w_c, 0);
    ctx.quadraticCurveTo(w - w_c, h * 0.3, w - w_c * 0.3, h * 0.6);
    ctx.quadraticCurveTo(w - w_c * 0.3, h * 0.8, w - w_c * 0.7, h);
    ctx.lineTo(w, h);
    ctx.closePath();
    ctx.fill();

    ctx.beginPath();
    ctx.moveTo(w - w_c, 0);
    ctx.quadraticCurveTo(w - w_c, h * 0.3, w - w_c * 0.3, h * 0.6);
    ctx.quadraticCurveTo(w - w_c * 0.3, h * 0.8, w - w_c * 0.7, h);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(w, h * 0.58);
    ctx.lineTo(w - w_c * 0.35, h * 0.6);
    ctx.lineTo(w, h * 0.62);
    ctx.fillStyle = '#DAA520';
    ctx.fill();

    // 8. Top Valance
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = Math.max(5, h * 0.01);

    let numScallops = 9;
    let sw = w / numScallops;

    let valanceGrad = ctx.createLinearGradient(0, 0, w, 0);
    for (let i = 0; i <= numScallops * 2; i++) {
        valanceGrad.addColorStop(i / (numScallops * 2), i % 2 === 0 ? '#2b0000' : '#bc0000');
    }

    ctx.fillStyle = valanceGrad;
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(0, h_v);
    for (let i = 0; i < numScallops; i++) {
        ctx.quadraticCurveTo(i * sw + sw / 2, h_v * 1.5, (i + 1) * sw, h_v);
    }
    ctx.lineTo(w, 0);
    ctx.closePath();
    ctx.fill();

    ctx.beginPath();
    ctx.moveTo(0, h_v);
    for (let i = 0; i < numScallops; i++) {
        ctx.quadraticCurveTo(i * sw + sw / 2, h_v * 1.5, (i + 1) * sw, h_v);
    }
    ctx.strokeStyle = '#DAA520';
    ctx.stroke();

    ctx.fillStyle = '#DAA520';
    ctx.lineWidth = Math.max(1, h * 0.004);
    for (let i = 0; i <= numScallops; i++) {
        ctx.beginPath();
        ctx.moveTo(i * sw, h_v);
        ctx.lineTo(i * sw, h_v * 1.2);
        ctx.stroke();

        ctx.beginPath();
        ctx.arc(i * sw, h_v * 1.25, ctx.lineWidth * 2, 0, Math.PI * 2);
        ctx.fill();
    }

    // Reset shadow
    ctx.shadowColor = 'transparent';
    ctx.shadowBlur = 0;
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = 0;

    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 Theater Maker transforms your photos into dramatic stage displays. It places your uploaded image onto a movie screen within a detailed theater setting, complete with red curtains, a wooden stage floor, brick backdrops, footlights, and adjustable spotlight effects. This tool is perfect for creating unique social media posts, cinematic presentations, or fun digital keepsakes that give your pictures a grand, theatrical flair.

Leave a Reply

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