Real-Time AI Speech & Voice Synthesizer App

An interactive Web Speech API text-to-speech voice generator featuring custom speech pitch, rate sliders, dynamic audio wave visualizer, and script exporter.

Interactive Mini-Tools

Real-Time AI Speech & Voice Synthesizer App

283 views • 122 downloads

🎙️ Executive Project Overview

The Real-Time AI Speech & Voice Synthesizer App is an interactive web-based text-to-speech voice generator built with the W3C Web Speech API standard. Designed for content creators, podcasters, and web developers, it enables real-time text-to-speech audio synthesis with custom pitch modulation, playback speed rate control, voice model selection, and an animated audio waveform visualizer.

🗣️

Multi-Voice & Accent Model Selector

Dynamically enumerates native system and browser voice models (US English, UK English, Male/Female accents) using `window.speechSynthesis.getVoices()`.

⚙️

Pitch, Rate & Volume Parameters

Fine-tune speech rate speed (0.5x to 2.0x), voice pitch frequency (0.5 to 1.5), and output volume levels with real-time numeric readouts.

🌊

Audio Waveform Visualizer

HTML5 Canvas sine wave animation that dynamically pulses during active speech playback and pauses when speech synthesis finishes.

📜

Sample Script Presets & Copy Action

Pre-loaded sample scripts (Tech Product Promo, AI Podcast Intro, Developer Tutorial) with 1-click clipboard text copying.

Key Advantages & Benefits

  • Zero Server Latency: Uses native browser SpeechSynthesis engines without relying on expensive third-party paid TTS APIs.
  • Web Accessibility Standard: Perfect utility for implementing screen-reader accessibility features on websites.
  • Cross-Platform Compatibility: Runs seamlessly across Google Chrome, Apple Safari, Mozilla Firefox, and Microsoft Edge.
  • Full Open Source Code: Includes complete HTML, CSS, and JS source code files with full customization rights.

🛠️ How to Use the Voice Synthesizer

  1. Type or paste your text script into the Speech Script Editor box.
  2. Select your preferred Voice Model from the dropdown menu.
  3. Adjust the Speech Speed and Voice Pitch sliders according to your preference.
  4. Click Synthesize Speech to listen to the audio output in real-time.
  5. Use the Pause or Stop buttons to control playback.
<!DOCTYPE html>
<html lang="en" class="h-full dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Real-Time AI Speech & Voice Synthesizer App - Web App</title>
    
    <!-- Fonts & Icons -->
    <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&family=Inter:wght@400;500;600;700&family=Outfit:wght@600;700;800&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
    
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            darkMode: 'class',
            theme: {
                extend: {
                    fontFamily: {
                        sans: ['Inter', 'sans-serif'],
                        outfit: ['Outfit', 'sans-serif'],
                        mono: ['Fira Code', 'monospace']
                    }
                }
            }
        }
    </script>
    <style>
        body { margin: 0; padding: 0; min-height: 100vh; background-color: #0b0f17; }
        ::-webkit-scrollbar { width: 6px; }
        ::-webkit-scrollbar-track { background: #0b0f17; }
        ::-webkit-scrollbar-thumb { background: rgba(56, 189, 248, 0.2); border-radius: 4px; }
    </style>
</head>
<body class="min-h-full text-slate-100 flex flex-col font-sans select-none bg-slate-950">

    <!-- Header Navigation -->
    <header class="h-16 bg-slate-900/90 backdrop-blur-md border-b border-slate-800 px-4 sm:px-6 flex items-center justify-between shrink-0 z-30 sticky top-0">
        <div class="flex items-center gap-3">
            <div class="w-9 h-9 rounded-xl bg-gradient-to-tr from-cyan-500 to-blue-600 flex items-center justify-center text-slate-950 font-outfit font-extrabold text-lg shadow-lg shadow-cyan-500/20">
                🎙️
            </div>
            <div>
                <h1 class="font-outfit font-bold text-sm sm:text-base text-white tracking-wide">Real-Time AI Voice & Speech Synthesizer</h1>
                <p class="text-[10px] text-cyan-400 font-mono">Web Speech API Voice Generator</p>
            </div>
        </div>

        <div class="flex items-center gap-2">
            <button id="copyScriptBtn" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-white font-outfit font-bold text-xs transition flex items-center gap-1.5 border border-slate-700">
                <i class="fa-solid fa-copy text-cyan-400"></i>
                <span>Copy Text</span>
            </button>
        </div>
    </header>

    <!-- Main Workspace -->
    <main class="flex-1 p-4 sm:p-6 max-w-6xl w-full mx-auto grid grid-cols-1 lg:grid-cols-3 gap-6">

        <!-- Left 2 Cols: Text Input & Audio Waveform Visualizer -->
        <div class="lg:col-span-2 space-y-6">
            
            <!-- Text Editor Drawer -->
            <div class="bg-slate-900 border border-slate-800 rounded-3xl p-5 sm:p-6 shadow-xl space-y-4">
                <div class="flex items-center justify-between border-b border-slate-800/80 pb-3">
                    <h2 class="font-outfit font-extrabold text-lg text-white flex items-center gap-2">
                        <i class="fa-solid fa-pen-to-square text-cyan-400"></i> Speech Script Editor
                    </h2>

                    <!-- Presets Selector -->
                    <select id="samplePresetSelect" class="bg-slate-950 border border-slate-800 text-slate-300 text-xs font-semibold rounded-xl px-3 py-1.5 focus:outline-none focus:border-cyan-500">
                        <option value="tech-promo">Preset: Tech Product Promo</option>
                        <option value="podcast-intro">Preset: AI Podcast Intro</option>
                        <option value="coding-tutorial">Preset: Developer Tutorial</option>
                    </select>
                </div>

                <textarea id="speechText" rows="7" class="w-full bg-slate-950 border border-slate-800/80 rounded-2xl p-4 text-slate-200 text-sm leading-relaxed focus:outline-none focus:border-cyan-500 transition resize-none font-mono" placeholder="Type or paste text to synthesize into voice speech..."></textarea>

                <!-- Audio Wave Visualizer Canvas -->
                <div class="bg-slate-950 border border-slate-800/80 rounded-2xl p-4 h-24 relative overflow-hidden flex flex-col justify-between">
                    <div class="flex items-center justify-between text-xs text-slate-400 font-mono">
                        <span class="flex items-center gap-1.5 text-cyan-400 font-bold">
                            <span id="statusIndicator" class="w-2 h-2 rounded-full bg-slate-600"></span>
                            <span id="statusText">Ready</span>
                        </span>
                        <span id="charCount" class="text-slate-500">0 characters</span>
                    </div>

                    <canvas id="waveCanvas" class="w-full h-12"></canvas>
                </div>
            </div>

            <!-- Playback Control Toolbar -->
            <div class="bg-slate-900 border border-slate-800 rounded-3xl p-4 sm:p-5 shadow-xl flex items-center justify-between gap-4">
                <div class="flex items-center gap-3">
                    <button id="playBtn" class="px-5 py-3 rounded-2xl bg-cyan-500 hover:bg-cyan-400 text-slate-950 font-outfit font-extrabold text-xs uppercase tracking-wider transition flex items-center gap-2 shadow-lg shadow-cyan-500/20">
                        <i class="fa-solid fa-play"></i>
                        <span>Synthesize Speech</span>
                    </button>

                    <button id="pauseBtn" class="p-3 rounded-2xl bg-slate-800 hover:bg-slate-700 text-slate-300 transition" title="Pause / Resume">
                        <i class="fa-solid fa-pause"></i>
                    </button>

                    <button id="stopBtn" class="p-3 rounded-2xl bg-slate-800 hover:bg-rose-900/40 text-rose-400 transition" title="Stop Playback">
                        <i class="fa-solid fa-square"></i>
                    </button>
                </div>

                <div class="text-xs text-slate-400 font-mono hidden sm:block">
                    Web Speech Engine 2.0
                </div>
            </div>
        </div>

        <!-- Right Col: Voice Tuning & Parameters -->
        <div class="bg-slate-900 border border-slate-800 rounded-3xl p-5 sm:p-6 shadow-xl space-y-6 flex flex-col justify-between">
            
            <div class="space-y-6">
                <!-- Voice Accent Selection -->
                <div class="space-y-3">
                    <label class="block font-outfit font-bold text-xs uppercase tracking-wider text-cyan-400">
                        🗣️ Select Voice Model
                    </label>
                    <select id="voiceSelect" class="w-full bg-slate-950 border border-slate-800 text-xs font-semibold text-slate-200 rounded-xl px-3 py-2.5 focus:outline-none focus:border-cyan-500">
                        <option value="">Loading Browser Voices...</option>
                    </select>
                </div>

                <!-- Speech Rate (Speed) -->
                <div class="space-y-2">
                    <div class="flex justify-between text-xs font-bold text-slate-300">
                        <span>Speech Speed (Rate)</span>
                        <span id="rateVal" class="font-mono text-cyan-400">1.0x</span>
                    </div>
                    <input type="range" id="rateInput" min="0.5" max="2.0" step="0.1" value="1.0" class="w-full accent-cyan-500">
                </div>

                <!-- Speech Pitch -->
                <div class="space-y-2">
                    <div class="flex justify-between text-xs font-bold text-slate-300">
                        <span>Voice Pitch</span>
                        <span id="pitchVal" class="font-mono text-cyan-400">1.0</span>
                    </div>
                    <input type="range" id="pitchInput" min="0.5" max="1.5" step="0.1" value="1.0" class="w-full accent-cyan-500">
                </div>

                <!-- Volume Level -->
                <div class="space-y-2">
                    <div class="flex justify-between text-xs font-bold text-slate-300">
                        <span>Output Volume</span>
                        <span id="volumeVal" class="font-mono text-cyan-400">100%</span>
                    </div>
                    <input type="range" id="volumeInput" min="0" max="1" step="0.1" value="1.0" class="w-full accent-cyan-500">
                </div>
            </div>

            <div class="p-3.5 bg-slate-950 border border-slate-800 rounded-2xl text-[11px] text-slate-400 space-y-1">
                <div class="text-white font-bold">Speech Engine Specs:</div>
                <div>API: W3C Web Speech API</div>
                <div>Status: Client-Side Synthesis</div>
            </div>
        </div>

    </main>

    <!-- JS Logic -->
    <script>
        const speechText = document.getElementById('speechText');
        const voiceSelect = document.getElementById('voiceSelect');
        const rateInput = document.getElementById('rateInput');
        const pitchInput = document.getElementById('pitchInput');
        const volumeInput = document.getElementById('volumeInput');
        const playBtn = document.getElementById('playBtn');
        const pauseBtn = document.getElementById('pauseBtn');
        const stopBtn = document.getElementById('stopBtn');
        const statusIndicator = document.getElementById('statusIndicator');
        const statusText = document.getElementById('statusText');
        const charCount = document.getElementById('charCount');
        const waveCanvas = document.getElementById('waveCanvas');

        const synth = window.speechSynthesis;
        let voices = [];
        let isSpeaking = false;

        // Sample Presets
        const presets = {
            'tech-promo': 'Welcome to TechPortal! Experience next-generation open-source developer tools, interactive web applications, and live tech news updates directly inside your web browser.',
            'podcast-intro': 'Hello everyone, welcome back to another episode of Future Tech Weekly. Today we are diving into AI prompt engineering and modern web development architectures.',
            'coding-tutorial': 'In this tutorial, we will learn how to build an interactive Web Speech synthesizer using pure HTML5, CSS3, and JavaScript.'
        };

        // Populate Voices Dropdown
        function populateVoices() {
            voices = synth.getVoices();
            voiceSelect.innerHTML = '';
            
            if (voices.length === 0) {
                const option = document.createElement('option');
                option.innerText = 'Standard Native Voice';
                voiceSelect.appendChild(option);
                return;
            }

            voices.forEach((voice, i) => {
                const option = document.createElement('option');
                option.value = i;
                option.innerText = `${voice.name} (${voice.lang})`;
                if (voice.default) option.selected = true;
                voiceSelect.appendChild(option);
            });
        }

        populateVoices();
        if (speechSynthesis.onvoiceschanged !== undefined) {
            speechSynthesis.onvoiceschanged = populateVoices;
        }

        // Waveform Visualizer Animation
        const ctx = waveCanvas.getContext('2d');
        let animationFrame;

        function drawWave() {
            ctx.clearRect(0, 0, waveCanvas.width, waveCanvas.height);
            ctx.lineWidth = 2;
            ctx.strokeStyle = isSpeaking ? '#06b6d4' : '#334155';
            ctx.beginPath();

            const sliceWidth = waveCanvas.width / 40;
            let x = 0;

            for (let i = 0; i < 40; i++) {
                const v = isSpeaking ? Math.sin(Date.now() * 0.01 + i) * 15 + waveCanvas.height / 2 : waveCanvas.height / 2;
                if (i === 0) ctx.moveTo(x, v);
                else ctx.lineTo(x, v);
                x += sliceWidth;
            }

            ctx.stroke();
            if (isSpeaking) animationFrame = requestAnimationFrame(drawWave);
        }

        // Synthesize Play Logic
        function speak() {
            if (synth.speaking) synth.cancel();

            const text = speechText.value.trim();
            if (!text) return;

            const utterance = new SpeechSynthesisUtterance(text);
            const selectedVoiceIndex = voiceSelect.value;
            if (voices[selectedVoiceIndex]) utterance.voice = voices[selectedVoiceIndex];

            utterance.rate = parseFloat(rateInput.value);
            utterance.pitch = parseFloat(pitchInput.value);
            utterance.volume = parseFloat(volumeInput.value);

            utterance.onstart = () => {
                isSpeaking = true;
                statusIndicator.className = 'w-2 h-2 rounded-full bg-cyan-400 animate-ping';
                statusText.innerText = 'Synthesizing Speech...';
                drawWave();
            };

            utterance.onend = () => {
                isSpeaking = false;
                statusIndicator.className = 'w-2 h-2 rounded-full bg-emerald-400';
                statusText.innerText = 'Finished';
                cancelAnimationFrame(animationFrame);
                drawWave();
            };

            utterance.onerror = () => {
                isSpeaking = false;
                statusIndicator.className = 'w-2 h-2 rounded-full bg-rose-500';
                statusText.innerText = 'Error';
            };

            synth.speak(utterance);
        }

        playBtn.addEventListener('click', speak);

        pauseBtn.addEventListener('click', () => {
            if (synth.speaking) {
                if (synth.paused) {
                    synth.resume();
                    statusText.innerText = 'Resumed';
                } else {
                    synth.pause();
                    statusText.innerText = 'Paused';
                }
            }
        });

        stopBtn.addEventListener('click', () => {
            if (synth.speaking) {
                synth.cancel();
                isSpeaking = false;
                statusIndicator.className = 'w-2 h-2 rounded-full bg-slate-600';
                statusText.innerText = 'Stopped';
            }
        });

        // UI Event Updates
        speechText.addEventListener('input', () => {
            charCount.innerText = speechText.value.length + ' characters';
        });

        rateInput.addEventListener('input', (e) => {
            document.getElementById('rateVal').innerText = parseFloat(e.target.value).toFixed(1) + 'x';
        });

        pitchInput.addEventListener('input', (e) => {
            document.getElementById('pitchVal').innerText = parseFloat(e.target.value).toFixed(1);
        });

        volumeInput.addEventListener('input', (e) => {
            document.getElementById('volumeVal').innerText = Math.round(e.target.value * 100) + '%';
        });

        document.getElementById('samplePresetSelect').addEventListener('change', (e) => {
            if (presets[e.target.value]) {
                speechText.value = presets[e.target.value];
                charCount.innerText = speechText.value.length + ' characters';
            }
        });

        document.getElementById('copyScriptBtn').addEventListener('click', () => {
            navigator.clipboard.writeText(speechText.value);
            alert('Speech script text copied to clipboard!');
        });

        // Init Default Text
        speechText.value = presets['tech-promo'];
        charCount.innerText = speechText.value.length + ' characters';
        drawWave();
    </script>
</body>
</html>
/* Supports both Dark and Light mode via Tailwind class variant */
// Interactive scripts included inside demo view file

Related Projects

Interactive Drag & Drop Kanban Task Board

Featured

A clean Trello-style Kanban board built with HTML5 HTML Drag and Drop API, color-coded task priorities, and local storage state persistence.

Interactive Mini-Tools • 60

CSS Glassmorphism & Gradient Code Generator

Featured

An interactive visual CSS generator tool to tweak backdrop blur, opacity, transparency, and background gradients with instant 1-click code copying.

Interactive Mini-Tools • 96

Markdown Live Preview Editor & PDF Exporter

Featured

A split-screen markdown writing tool rendering styled HTML live as you type with one-click HTML copy export.

Interactive Mini-Tools • 75