Please bookmark this page to avoid losing your image tool!

Video To ASCII Art Converter

(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, outputWidth = 100, charSet = ' .:-=+*#%@') {
    // Validate parameters
    outputWidth = Number(outputWidth);
    if (isNaN(outputWidth) || outputWidth <= 10) outputWidth = 100;
    if (typeof charSet !== 'string' || charSet.length === 0) charSet = ' .:-=+*#%@';

    // 1. Setup the main container
    const container = document.createElement('div');
    container.style.width = '100%';
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';
    container.style.backgroundColor = '#1e1e1e';
    container.style.color = '#f1f1f1';
    container.style.fontFamily = 'system-ui, sans-serif';
    container.style.padding = '20px';
    container.style.boxSizing = 'border-box';
    container.style.borderRadius = '8px';
    container.style.boxShadow = '0 4px 10px rgba(0,0,0,0.5)';

    // 2. Title
    const title = document.createElement('h3');
    title.textContent = 'Video To ASCII Art Converter';
    title.style.marginTop = '0';
    title.style.marginBottom = '20px';
    title.style.textAlign = 'center';
    container.appendChild(title);

    // 3. User Controls
    const controls = document.createElement('div');
    controls.style.display = 'flex';
    controls.style.gap = '15px';
    controls.style.marginBottom = '20px';
    controls.style.alignItems = 'center';
    controls.style.flexWrap = 'wrap';
    controls.style.justifyContent = 'center';

    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = 'video/*';
    fileInput.style.padding = '6px';
    fileInput.style.backgroundColor = '#333';
    fileInput.style.border = '1px solid #555';
    fileInput.style.borderRadius = '4px';
    fileInput.style.color = '#fff';
    fileInput.style.cursor = 'pointer';

    const playBtn = document.createElement('button');
    playBtn.textContent = 'Play / Pause';
    playBtn.style.padding = '8px 16px';
    playBtn.style.backgroundColor = '#4CAF50';
    playBtn.style.color = '#fff';
    playBtn.style.border = 'none';
    playBtn.style.borderRadius = '4px';
    playBtn.style.cursor = 'pointer';
    playBtn.style.fontWeight = 'bold';
    playBtn.disabled = true;

    // Hover effect for the button
    playBtn.addEventListener('mouseover', () => { if (!playBtn.disabled) playBtn.style.backgroundColor = '#45a049'; });
    playBtn.addEventListener('mouseout', () => { if (!playBtn.disabled) playBtn.style.backgroundColor = '#4CAF50'; });

    const colorWrapper = document.createElement('div');
    colorWrapper.style.display = 'flex';
    colorWrapper.style.alignItems = 'center';
    colorWrapper.style.gap = '5px';
    const colorLabel = document.createElement('span');
    colorLabel.textContent = 'ASCII Color:';
    colorLabel.style.fontSize = '14px';
    const colorInput = document.createElement('input');
    colorInput.type = 'color';
    colorInput.value = '#00ff41'; // Matrix green default
    colorInput.style.cursor = 'pointer';
    colorInput.style.border = 'none';
    colorInput.style.padding = '0';
    colorInput.style.background = 'none';

    colorWrapper.appendChild(colorLabel);
    colorWrapper.appendChild(colorInput);

    controls.appendChild(fileInput);
    controls.appendChild(playBtn);
    controls.appendChild(colorWrapper);
    container.appendChild(controls);

    // 4. ASCII Render Display
    const asciiContainer = document.createElement('div');
    asciiContainer.style.background = '#0a0a0a';
    asciiContainer.style.padding = '10px';
    asciiContainer.style.borderRadius = '6px';
    asciiContainer.style.overflowX = 'auto';
    asciiContainer.style.maxWidth = '100%';
    asciiContainer.style.border = '1px solid #333';

    const pre = document.createElement('pre');
    pre.style.margin = '0';
    pre.style.fontFamily = '"Courier New", Courier, monospace';
    pre.style.fontSize = '8px';
    pre.style.lineHeight = '8px';
    pre.style.whiteSpace = 'pre';
    pre.style.color = colorInput.value;
    
    asciiContainer.appendChild(pre);
    container.appendChild(asciiContainer);

    // Apply color changes dynamically
    colorInput.addEventListener('input', (e) => {
        pre.style.color = e.target.value;
    });

    // 5. Logic Core setup
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    
    const video = document.createElement('video');
    video.muted = true;
    video.loop = true;
    video.playsInline = true;

    // Convert pixel inputs to ASCII characters
    function toAscii(source, sWidth, sHeight) {
        if (!sWidth || !sHeight) return '';

        // Terminal text assumes half-aspect ratio visually.
        // We calculate rows via 0.5 ratio to ensure the resulting text image isn't overly stretched.
        const ratio = sHeight / sWidth;
        const width = outputWidth;
        const height = Math.max(1, Math.floor(width * ratio * 0.5));

        canvas.width = width;
        canvas.height = height;

        try {
            ctx.drawImage(source, 0, 0, width, height);
            const imgData = ctx.getImageData(0, 0, width, height).data;
            const charLen = charSet.length - 1;

            let asciiStr = '';
            for (let y = 0; y < height; y++) {
                for (let x = 0; x < width; x++) {
                    const offset = (y * width + x) * 4;
                    const r = imgData[offset];
                    const g = imgData[offset + 1];
                    const b = imgData[offset + 2];
                    
                    // Grayscale perceptual luminance
                    const brightness = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
                    const charIdx = Math.floor(brightness * charLen);
                    asciiStr += charSet[charIdx];
                }
                asciiStr += '\n';
            }
            return asciiStr;
        } catch (e) {
            return `Canvas read error (Cross-Origin restricted). \nPlease upload your own video using the button above.`;
        }
    }

    // Attempt to render the provided original fallback image while waiting for user interaction
    if (originalImg) {
        const drawInitial = () => {
            const w = originalImg.naturalWidth || originalImg.width;
            const h = originalImg.naturalHeight || originalImg.height;
            if (w && h) pre.textContent = toAscii(originalImg, w, h);
        };
        if (originalImg.complete) drawInitial();
        else originalImg.addEventListener('load', drawInitial);
    } else {
        pre.textContent = 'Upload a video to see ASCII magic!';
    }

    // 6. Video Integration & Animation Loop
    let animationId = null;

    function renderLoop() {
        // Free resources automatically if the returned container is completely removed from the DOM
        if (container.isConnected === false) {
            video.pause();
            video.src = '';
            return;
        }

        if (!video.paused && !video.ended) {
            pre.textContent = toAscii(video, video.videoWidth, video.videoHeight);
        }
        animationId = requestAnimationFrame(renderLoop);
    }

    fileInput.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (!file) return;

        const url = URL.createObjectURL(file);
        video.src = url;
        video.load();

        video.onloadeddata = () => {
            playBtn.disabled = false;
            playBtn.style.backgroundColor = '#4CAF50';
            video.play()
                .then(() => { if (!animationId) renderLoop(); })
                .catch(() => {
                    // Modern browsers might delay autoplay policies
                    console.info('Click "Play / Pause" to begin the video.');
                });
        };
    });

    playBtn.addEventListener('click', () => {
        if (video.paused || video.ended) {
            video.play();
            if (!animationId) renderLoop();
        } else {
            video.pause();
        }
    });

    return container;
}

Free Image Tool Creator

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

Description

This tool converts uploaded video files into real-time ASCII art animations. It processes video frames by translating pixel brightness into a sequence of text characters, creating a stylized, retro aesthetic. Users can customize the experience by choosing the color of the ASCII characters and controlling video playback. This tool is ideal for creators looking to add a lo-fi or hacker-style effect to their video content, making it perfect for digital art projects, social media filters, or unique visual presentations.

Leave a Reply

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