Please bookmark this page to avoid losing your image tool!

Photo Sound Maker And Toy Noise Generator

(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, baseVolume = "0.5") {
    // Parse volume and clamp it between 0.0 and 1.0
    const volume = Math.max(0, Math.min(parseFloat(baseVolume) || 0.5, 1));
    
    // Create the main container
    const container = document.createElement('div');
    container.style.position = 'relative';
    container.style.display = 'inline-block';
    container.style.userSelect = 'none';
    container.style.overflow = 'hidden';
    container.style.lineHeight = '0'; // Prevent bottom spacing issues
    container.style.borderRadius = '8px';
    container.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';

    // Create the interactive canvas
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    canvas.style.cursor = 'crosshair';
    canvas.style.maxWidth = '100%';
    canvas.style.display = 'block';
    canvas.style.touchAction = 'none'; // Prevent scrolling when tapping
    
    container.appendChild(canvas);
    const ctx = canvas.getContext('2d');
    
    // Audio Context and state
    let audioCtx = null;
    let interacted = false;
    let particles = [];
    
    // Toy Sound Synthesis Generators
    const soundGenerators = {
        squeak: () => {
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.type = 'triangle';
            osc.frequency.setValueAtTime(600, audioCtx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(1400, audioCtx.currentTime + 0.1);
            osc.frequency.exponentialRampToValueAtTime(600, audioCtx.currentTime + 0.2);
            gain.gain.setValueAtTime(volume, audioCtx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.2);
            osc.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start(); osc.stop(audioCtx.currentTime + 0.2);
        },
        boing: () => {
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.type = 'sine';
            osc.frequency.setValueAtTime(150, audioCtx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(800, audioCtx.currentTime + 0.05);
            osc.frequency.exponentialRampToValueAtTime(100, audioCtx.currentTime + 0.4);
            gain.gain.setValueAtTime(volume * 1.2, audioCtx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.4);
            osc.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start(); osc.stop(audioCtx.currentTime + 0.4);
        },
        honk: () => {
            const osc = audioCtx.createOscillator();
            const filter = audioCtx.createBiquadFilter();
            const gain = audioCtx.createGain();
            osc.type = 'sawtooth';
            osc.frequency.setValueAtTime(250 + Math.random() * 50, audioCtx.currentTime);
            filter.type = 'lowpass';
            filter.frequency.setValueAtTime(2000, audioCtx.currentTime);
            filter.frequency.exponentialRampToValueAtTime(200, audioCtx.currentTime + 0.25);
            gain.gain.setValueAtTime(0, audioCtx.currentTime);
            gain.gain.linearRampToValueAtTime(volume * 0.8, audioCtx.currentTime + 0.05);
            gain.gain.linearRampToValueAtTime(0.01, audioCtx.currentTime + 0.3);
            osc.connect(filter);
            filter.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start(); osc.stop(audioCtx.currentTime + 0.3);
        },
        beep: () => {
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.type = 'square';
            osc.frequency.setValueAtTime(700 + Math.random() * 600, audioCtx.currentTime);
            gain.gain.setValueAtTime(volume * 0.4, audioCtx.currentTime);
            gain.gain.setValueAtTime(0, audioCtx.currentTime + 0.15);
            osc.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start(); osc.stop(audioCtx.currentTime + 0.15);
        },
        rattle: () => {
            const buffSize = audioCtx.sampleRate * 0.4;
            const buffer = audioCtx.createBuffer(1, buffSize, audioCtx.sampleRate);
            const data = buffer.getChannelData(0);
            for (let i = 0; i < buffSize; i++) data[i] = Math.random() * 2 - 1;
            
            const noise = audioCtx.createBufferSource();
            noise.buffer = buffer;
            
            const amGain = audioCtx.createGain();
            const lfo = audioCtx.createOscillator();
            lfo.type = 'square';
            lfo.frequency.value = 15;
            lfo.connect(amGain.gain);
            
            const masterGain = audioCtx.createGain();
            masterGain.gain.setValueAtTime(volume * 0.7, audioCtx.currentTime);
            masterGain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.4);
            
            noise.connect(amGain);
            amGain.connect(masterGain);
            masterGain.connect(audioCtx.destination);
            
            noise.start(); lfo.start();
            noise.stop(audioCtx.currentTime + 0.4); lfo.stop(audioCtx.currentTime + 0.4);
        }
    };

    const toys = [
        { id: 'squeak', text: "SQUEAK!", color: "#FFE800", shape: "circle" },
        { id: 'boing', text: "BOING!", color: "#00FF66", shape: "star" },
        { id: 'honk', text: "HONK!", color: "#FF3366", shape: "cloud" },
        { id: 'beep', text: "BEEP!", color: "#33CCFF", shape: "square" },
        { id: 'rattle', text: "RATTLE!", color: "#FF9900", shape: "star" }
    ];

    // Interaction Handler
    function handleInteraction(e) {
        e.preventDefault();
        
        // Initialize Audio context on first interaction
        if (!audioCtx) {
            audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        }
        if (audioCtx.state === 'suspended') {
            audioCtx.resume();
        }
        interacted = true;

        // Get coordinates
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        
        let clientX = e.clientX;
        let clientY = e.clientY;
        
        if (e.touches && e.touches.length > 0) {
            clientX = e.touches[0].clientX;
            clientY = e.touches[0].clientY;
        }

        const x = (clientX - rect.left) * scaleX;
        const y = (clientY - rect.top) * scaleY;

        // Pick a random toy noise
        const toy = toys[Math.floor(Math.random() * toys.length)];
        
        // Play the sound
        soundGenerators[toy.id]();

        // Add visual particle
        particles.push({
            x: x,
            y: y,
            life: 1.0,
            text: toy.text,
            color: toy.color,
            shape: toy.shape,
            rotation: (Math.random() - 0.5) * 0.6,
            scale: 0.8 + Math.random() * 0.4
        });
    }

    // Helper to draw geometric shape
    function drawShape(ctx, shape) {
        ctx.beginPath();
        if (shape === 'star') {
            const spikes = 8;
            const outerRadius = 45;
            const innerRadius = 30;
            let rot = Math.PI / 2 * 3;
            let step = Math.PI / spikes;
            
            ctx.moveTo(0, -outerRadius);
            for (let i = 0; i < spikes; i++) {
                ctx.lineTo(Math.cos(rot) * outerRadius, Math.sin(rot) * outerRadius);
                rot += step;
                ctx.lineTo(Math.cos(rot) * innerRadius, Math.sin(rot) * innerRadius);
                rot += step;
            }
            ctx.lineTo(0, -outerRadius);
        } else if (shape === 'circle') {
            ctx.arc(0, 0, 40, 0, Math.PI * 2);
        } else if (shape === 'cloud') {
            ctx.ellipse(0, 0, 50, 35, 0, 0, Math.PI * 2);
        } else if (shape === 'square') {
            ctx.rect(-40, -40, 80, 80);
        }
        ctx.closePath();
        ctx.fill();
        ctx.lineWidth = 4;
        ctx.strokeStyle = 'black';
        ctx.stroke();
    }

    // Render loop
    function render() {
        // Only constantly redraw if active particles exist or prior to first interaction
        if (interacted && particles.length === 0) {
            requestAnimationFrame(render);
            return;
        }

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(originalImg, 0, 0);

        if (!interacted) {
            // Draw instructional overlay
            ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            ctx.fillStyle = 'white';
            const fontSize = Math.max(16, canvas.width * 0.05);
            ctx.font = `bold ${fontSize}px "Comic Sans MS", "Chalkboard SE", sans-serif`;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            
            // Text shadow for readability
            ctx.shadowColor = 'black';
            ctx.shadowBlur = 10;
            ctx.shadowOffsetX = 2;
            ctx.shadowOffsetY = 2;
            ctx.fillText('Click to play Toy Noises!', canvas.width / 2, canvas.height / 2);
            ctx.shadowBlur = 0;
            ctx.shadowOffsetX = 0;
            ctx.shadowOffsetY = 0;
        }

        // Draw and update active particles
        for (let i = particles.length - 1; i >= 0; i--) {
            const p = particles[i];
            
            ctx.save();
            ctx.translate(p.x, p.y);
            ctx.rotate(p.rotation);
            
            // Pop out and float up
            const s = p.scale * (1.2 - p.life * 0.2); 
            ctx.scale(s, s);
            ctx.globalAlpha = p.life;
            p.y -= 1.5; // Float upwards slightly

            // Draw Toy Visual Marker
            ctx.fillStyle = p.color;
            drawShape(ctx, p.shape);

            // Draw Toy Text Inside
            ctx.fillStyle = 'black';
            ctx.font = '900 18px "Comic Sans MS", "Chalkboard SE", sans-serif';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(p.text, 0, 0);

            ctx.restore();

            p.life -= 0.015; // Fade out speed
            if (p.life <= 0) {
                particles.splice(i, 1);
            }
        }

        // Keep redrawing empty frame to clean up once completed
        if (interacted && particles.length === 0) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(originalImg, 0, 0);
        }

        requestAnimationFrame(render);
    }

    // Attach listeners
    canvas.addEventListener('mousedown', handleInteraction);
    canvas.addEventListener('touchstart', handleInteraction, {passive: false});

    // Start render loop
    requestAnimationFrame(render);

    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 interactive tool transforms any uploaded image into a playful, touch-sensitive soundboard. By clicking or tapping on the image, users trigger a variety of synthesized toy-like sound effects—such as squeaks, boings, honks, beeps, and rattles—accompanied by vibrant, animated visual particles. It is an ideal tool for creating engaging digital activities for children, adding an element of gamified fun to photos, or developing interactive educational content.

Leave a Reply

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