| ```html |
| <!DOCTYPE html> |
| <html lang="hi"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Cartesia AI TTS Tool</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;700&display=swap'); |
| body { |
| font-family: 'Plus Jakarta Sans', sans-serif; |
| background-color: #050505; |
| color: #ffffff; |
| } |
| .cartesia-card { |
| background: rgba(20, 20, 20, 0.8); |
| border: 1px solid #333; |
| backdrop-filter: blur(10px); |
| } |
| .status-pill { |
| padding: 2px 8px; |
| border-radius: 99px; |
| font-size: 0.7rem; |
| font-weight: bold; |
| text-transform: uppercase; |
| } |
| .pulse-red { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); animation: pulse-red 2s infinite; } |
| @keyframes pulse-red { 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); } 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } } |
| </style> |
| </head> |
| <body class="p-4 md:p-10 min-h-screen"> |
| <div class="max-w-5xl mx-auto"> |
| |
| <div class="flex flex-col md:flex-row justify-between items-center mb-10 gap-4"> |
| <div> |
| <h1 class="text-4xl font-extrabold tracking-tight text-white">Cartesia <span class="text-blue-500">AI</span></h1> |
| <p class="text-gray-400">High-performance text-to-speech engine</p> |
| </div> |
| <div class="flex items-center gap-3 cartesia-card p-2 px-4 rounded-full"> |
| <span id="statusDot" class="h-3 w-3 rounded-full bg-red-500 pulse-red"></span> |
| <span id="statusText" class="text-sm font-medium text-gray-300 uppercase tracking-widest">Disconnected</span> |
| </div> |
| </div> |
|
|
| <div class="grid grid-cols-1 lg:grid-cols-12 gap-8"> |
| |
| <div class="lg:col-span-4 space-y-6"> |
| <div class="cartesia-card p-6 rounded-3xl"> |
| <h2 class="text-lg font-bold mb-4 flex items-center gap-2"> |
| <span>🔑</span> API Settings |
| </h2> |
| <div class="space-y-4"> |
| <div> |
| <label class="block text-xs font-bold text-gray-500 mb-1">API KEY</label> |
| <input type="password" id="apiKey" placeholder="sk_car_..." |
| class="w-full bg-black border border-gray-800 rounded-xl p-3 text-sm focus:border-blue-500 outline-none transition-all"> |
| </div> |
| <button id="connectBtn" class="w-full bg-white text-black font-bold py-3 rounded-xl hover:bg-gray-200 transition-colors"> |
| Connect Account |
| </button> |
| </div> |
| </div> |
|
|
| <div class="cartesia-card p-6 rounded-3xl"> |
| <h2 class="text-lg font-bold mb-4 flex items-center gap-2"> |
| <span>🎙️</span> Voice Engine |
| </h2> |
| <div class="space-y-4"> |
| <div> |
| <label class="block text-xs font-bold text-gray-500 mb-1">CHOOSE VOICE</label> |
| <select id="voiceSelect" class="w-full bg-black border border-gray-800 rounded-xl p-3 text-sm outline-none focus:border-blue-500"> |
| <option value="">Select a voice...</option> |
| </select> |
| </div> |
| <div> |
| <label class="block text-xs font-bold text-gray-500 mb-1">CUSTOM VOICE ID</label> |
| <input type="text" id="customVoiceId" placeholder="Paste Voice ID here..." |
| class="w-full bg-black border border-gray-800 rounded-xl p-3 text-sm outline-none focus:border-blue-500"> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="lg:col-span-8 space-y-6"> |
| |
| <div id="logBox" class="hidden cartesia-card p-4 rounded-2xl text-sm border-l-4"> |
| <p id="logMessage"></p> |
| </div> |
|
|
| <div class="cartesia-card p-8 rounded-3xl"> |
| <label class="block text-xs font-bold text-gray-500 mb-2 uppercase tracking-widest text-center">Your Script</label> |
| <textarea id="textInput" rows="6" placeholder="Yahan apna text likhen..." |
| class="w-full bg-transparent border-b border-gray-800 text-2xl text-center focus:border-blue-500 outline-none transition-all resize-none mb-8"></textarea> |
| |
| <div class="flex flex-col items-center gap-6"> |
| <button id="generateBtn" class="bg-blue-600 hover:bg-blue-500 text-white px-10 py-4 rounded-full font-bold text-lg shadow-lg shadow-blue-900/40 disabled:opacity-50 transition-all flex items-center gap-2"> |
| <span id="btnIcon">⚡</span> <span id="btnText">Convert to Speech</span> |
| </button> |
|
|
| <div id="audioArea" class="w-full hidden text-center animate-fade-in"> |
| <audio id="audioPlayer" controls class="w-full mx-auto"></audio> |
| <p class="text-xs text-gray-500 mt-2">Niche audio play karen</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| const apiKeyInput = document.getElementById('apiKey'); |
| const connectBtn = document.getElementById('connectBtn'); |
| const voiceSelect = document.getElementById('voiceSelect'); |
| const customVoiceId = document.getElementById('customVoiceId'); |
| const textInput = document.getElementById('textInput'); |
| const generateBtn = document.getElementById('generateBtn'); |
| const logBox = document.getElementById('logBox'); |
| const logMessage = document.getElementById('logMessage'); |
| const statusDot = document.getElementById('statusDot'); |
| const statusText = document.getElementById('statusText'); |
| const audioArea = document.getElementById('audioArea'); |
| const audioPlayer = document.getElementById('audioPlayer'); |
| |
| |
| const CARTESIA_API_URL = "https://api.cartesia.ai"; |
| |
| function updateLog(msg, type = 'info') { |
| logBox.classList.remove('hidden'); |
| logMessage.textContent = msg; |
| if (type === 'error') { |
| logBox.className = "cartesia-card p-4 rounded-2xl text-sm border-l-4 border-red-500 text-red-400"; |
| } else if (type === 'success') { |
| logBox.className = "cartesia-card p-4 rounded-2xl text-sm border-l-4 border-green-500 text-green-400"; |
| } else { |
| logBox.className = "cartesia-card p-4 rounded-2xl text-sm border-l-4 border-blue-500 text-blue-400"; |
| } |
| } |
| |
| async function fetchVoices() { |
| const key = apiKeyInput.value.trim(); |
| if (!key) return updateLog("Pehle API Key daalen", "error"); |
| |
| connectBtn.disabled = true; |
| connectBtn.textContent = "Connecting..."; |
| |
| try { |
| const response = await fetch(`${CARTESIA_API_URL}/voices`, { |
| headers: { |
| "X-API-Key": key, |
| "Cartesia-Version": "2024-06-10" |
| } |
| }); |
| |
| if (!response.ok) throw new Error("API Key galat hai ya expiry ho gayi hai."); |
| |
| const voices = await response.json(); |
| |
| voiceSelect.innerHTML = '<option value="">Select a voice...</option>'; |
| voices.forEach(voice => { |
| const option = document.createElement('option'); |
| option.value = voice.id; |
| option.textContent = `${voice.name} (${voice.language})`; |
| voiceSelect.appendChild(option); |
| }); |
| |
| statusDot.className = "h-3 w-3 rounded-full bg-green-500 shadow-lg shadow-green-900"; |
| statusText.textContent = "CONNECTED"; |
| updateLog("Account successfully connected! Voices load ho chuki hain.", "success"); |
| |
| } catch (err) { |
| updateLog(err.message, "error"); |
| statusDot.className = "h-3 w-3 rounded-full bg-red-500 pulse-red"; |
| statusText.textContent = "ERROR"; |
| } finally { |
| connectBtn.disabled = false; |
| connectBtn.textContent = "Connect Account"; |
| } |
| } |
| |
| connectBtn.addEventListener('click', fetchVoices); |
| |
| generateBtn.addEventListener('click', async () => { |
| const key = apiKeyInput.value.trim(); |
| const voiceId = customVoiceId.value.trim() || voiceSelect.value; |
| const text = textInput.value.trim(); |
| |
| if (!key) return updateLog("API Key missing!", "error"); |
| if (!voiceId) return updateLog("Koi voice select karen ya custom ID daalen", "error"); |
| if (!text) return updateLog("Kuch text to likhen!", "error"); |
| |
| generateBtn.disabled = true; |
| document.getElementById('btnText').textContent = "Processing..."; |
| document.getElementById('btnIcon').textContent = "⌛"; |
| audioArea.classList.add('hidden'); |
| |
| try { |
| const response = await fetch(`${CARTESIA_API_URL}/tts/bytes`, { |
| method: 'POST', |
| headers: { |
| "X-API-Key": key, |
| "Cartesia-Version": "2024-06-10", |
| "Content-Type": "application/json" |
| }, |
| body: JSON.stringify({ |
| model_id: "sonic-english", |
| transcript: text, |
| voice: { |
| mode: "id", |
| id: voiceId |
| }, |
| output_format: { |
| container: "wav", |
| encoding: "pcm_f32le", |
| sample_rate: 44100 |
| } |
| }) |
| }); |
| |
| if (!response.ok) { |
| const errorData = await response.json(); |
| throw new Error(errorData.message || "TTS generation failed"); |
| } |
| |
| const blob = await response.blob(); |
| const url = URL.createObjectURL(blob); |
| audioPlayer.src = url; |
| audioArea.classList.remove('hidden'); |
| updateLog("Audio generate ho gaya!", "success"); |
| audioPlayer.play(); |
| |
| } catch (err) { |
| updateLog(err.message, "error"); |
| } finally { |
| generateBtn.disabled = false; |
| document.getElementById('btnText').textContent = "Convert to Speech"; |
| document.getElementById('btnIcon').textContent = "⚡"; |
| } |
| }); |
| </script> |
| </body> |
| </html> |
|
|
| ``` |
|
|