| import { useEffect, useState } from "react"; |
| import { AutoModel, AutoProcessor } from "@huggingface/transformers"; |
|
|
| export default function App() { |
| const [model, setModel] = useState(null); |
| const [processor, setProcessor] = useState(null); |
| const [text, setText] = useState("Hello! This is a test of Chatterbox Turbo."); |
| const [loading, setLoading] = useState(false); |
|
|
| useEffect(() => { |
| async function loadModel() { |
| setLoading(true); |
| const m = await AutoModel.from_pretrained( |
| "spacekaren/chatterbox-turbo-webgpu", |
| { |
| device: "webgpu", |
| dtype: "q4f16", |
| } |
| ); |
|
|
| const p = await AutoProcessor.from_pretrained( |
| "spacekaren/chatterbox-turbo-webgpu" |
| ); |
|
|
| setModel(m); |
| setProcessor(p); |
| setLoading(false); |
| } |
|
|
| loadModel(); |
| }, []); |
|
|
| async function generateSpeech() { |
| if (!model || !processor) return; |
| setLoading(true); |
|
|
| const inputs = await processor(text); |
| const audio = await model.generate(inputs); |
|
|
| const blob = new Blob([audio], { type: "audio/wav" }); |
| const url = URL.createObjectURL(blob); |
|
|
| const audioEl = new Audio(url); |
| audioEl.play(); |
|
|
| setLoading(false); |
| } |
|
|
| return ( |
| <div className="min-h-screen flex flex-col items-center justify-center p-6 gap-4"> |
| <h1 className="text-2xl font-bold">Chatterbox Turbo WebGPU TTS</h1> |
| |
| <textarea |
| className="w-full max-w-xl p-3 border rounded-xl" |
| rows={4} |
| value={text} |
| onChange={(e) => setText(e.target.value)} |
| /> |
| |
| <button |
| onClick={generateSpeech} |
| disabled={loading || !model} |
| className="px-6 py-3 bg-black text-white rounded-2xl shadow" |
| > |
| {loading ? "Loading / Generating..." : "Generate Speech"} |
| </button> |
| |
| <p className="text-sm opacity-70"> |
| Runs fully in-browser using WebGPU (~539MB model) |
| </p> |
| </div> |
| ); |
| } |
|
|