Skip to content

Repository files navigation

LocalPrompt

A free, private teleprompter that follows your voice — running entirely in your browser.

LocalPrompt is a browser-based teleprompter for YouTubers, streamers, course creators, and anyone who presents to a camera. It listens as you speak and scrolls automatically to keep your place, with no foot pedal and no guessing at a fixed speed. Your script, settings, and recordings never leave your device — there are no accounts, no uploads, and no servers touching your content.

🔗 Live app: local-prompt.vercel.app


Features

  • Voice tracking — Uses the browser's Web Speech API to transcribe what you say in real time and a custom word-matching algorithm to align it to your script. Pause, ad-lib, or skip ahead, and the prompter follows and re-syncs.
  • Three scroll modes:
    • Voice — scrolls as you speak (default).
    • Auto — steady scroll at an adjustable speed (10–300 px/s), tunable live with arrow keys.
    • Manual — you control scrolling with the keyboard or trackpad.
  • Audio recording — Capture your take while reading via MediaRecorder, then play back, download (.webm / .ogg / .m4a), or delete. Recordings are reachable from both the editor and the prompter control bar.
  • Live settings preview — A scaled, pixel-faithful miniature of the prompter shows exactly how your script will look before you press Start, including the reading guide aligned to the active line.
  • Deep customization — Text size, line spacing, side margins, alignment, four color themes (Classic, Amber, Paper, Night Blue), a movable reading guide, and horizontal/vertical mirroring for beam-splitter rigs.
  • 100% local-first — Script and settings persist only in localStorage; recordings live in memory as object URLs until you download them. Nothing is sent anywhere.
  • Fullscreen, keyboard-driven prompting — Space to play/pause, arrows for speed/scroll, R to restart, Esc to exit.

Tech Stack

Layer Choice
Framework Next.js 16 (App Router)
Language TypeScript
UI React 19, Base UI primitives (shadcn-style wrappers)
Styling Tailwind CSS v4 (CSS-first config, design tokens in globals.css)
Icons lucide-react
Speech Web Speech API (SpeechRecognition / webkitSpeechRecognition)
Audio MediaRecorder + getUserMedia
Analytics @vercel/analytics
Image tooling sharp (favicon / OG generation at build time)

There is no backend and no database — the app is entirely client-side. Everything renders from a single route.


Architecture

Directory layout

app/
  layout.tsx          Root layout, fonts, full SEO metadata + JSON-LD
  page.tsx            Single route — renders the teleprompter app
  globals.css         Tailwind v4 theme tokens, dark theme, custom scrollbars
  robots.ts           Generated robots.txt
  sitemap.ts          Generated sitemap.xml

components/teleprompter/
  teleprompter-app.tsx   Top-level state container & mode switching
  settings-panel.tsx     Editor sidebar controls (sliders, toggles, themes)
  settings-preview.tsx   Scaled live preview of the prompter view
  prompter-view.tsx      Fullscreen scrolling reader + control bar
  recordings-list.tsx    Playback / download / delete UI for takes

components/ui/          Base UI primitive wrappers (button, slider, sheet, …)

hooks/
  use-speech-recognition.ts  Web Speech API session lifecycle + tracking
  use-recorder.ts            MediaRecorder lifecycle + recordings state

lib/
  prompter.ts          Types, themes, defaults, tokenizer, matching algorithm
  utils.ts             cn() class helper

State model

teleprompter-app.tsx is the single source of truth. It owns:

  • mode'edit' (script editor + settings) or 'prompt' (fullscreen reader).
  • script and settings — debounced into localStorage under a versioned key so a session restores on reload.
  • currentWordIndex — the active reading position, shared between the speech hook and the rendered view.
  • The useRecorder and useSpeechRecognition hook instances.

Settings are typed by PrompterSettings in lib/prompter.ts, which also defines the theme palette (PROMPTER_THEMES) and DEFAULT_SETTINGS.

Voice tracking algorithm

The hardest part of the app is keeping the reading position aligned to a noisy, real-time speech transcript. This lives in lib/prompter.ts and hooks/use-speech-recognition.ts.

  1. Tokenization (tokenizeScript) splits the script into tokens, preserving paragraph breaks and assigning a wordIndex to each speakable word. normalizeWord lowercases, strips diacritics and punctuation so comparisons are robust.
  2. Fuzzy word matching (wordsMatch) accepts a spoken word as a match when it is identical, shares a prefix on longer words (scrollscrolling), or differs by a single edit (capped edit-distance). This absorbs transcription quirks.
  3. Position advancement (advanceMatch) walks spoken words against a lookahead window of the script:
    • Exact match at the current position is always accepted.
    • Small forward jumps are accepted for content words, or for stopwords only when the next spoken word confirms the next script word (bigram confirmation).
    • Larger jumps within the window require bigram confirmation, preventing false jumps on common words.
    • After several consecutive misses, a wider re-sync search uses the last two missed words as a bigram to recover when the reader skips ahead or improvises.
  4. Commit vs. interim (in the hook): position is committed only from finalized transcript segments (which never change), while interim results drive a live preview that is re-evaluated from the committed state each time. Display position is kept monotonic so engine corrections never cause the text to jump backward.
  5. Session resilience: browser speech engines stop after silence, so the hook transparently restarts recognition while active, and tears down handlers with abort() on stop so a dying session can never restart a dead one.

Audio recording

use-recorder.ts wraps getUserMedia + MediaRecorder, preferring audio/webm;codecs=opus where supported and falling back gracefully. Each take is stored as a Blob plus an object URL with duration and timestamp. Downloads create a fresh object URL from the blob and use a DOM-attached anchor so they work across browsers (including Firefox). Status surfaces unsupported and denied states so the UI can explain what happened.

Privacy model

  • Script & settings: localStorage only, on the user's device.
  • Recordings: in-memory Blobs / object URLs, never persisted or uploaded; cleared on refresh unless downloaded.
  • Speech: handled by the browser's built-in engine. Note that some browsers (e.g. Chrome) may process audio via the vendor's cloud service as part of the Web Speech API — for fully offline use, choose auto or manual scroll mode. This caveat is surfaced in the app footer.

Getting Started

# install dependencies
pnpm install

# start the dev server
pnpm dev

Open http://localhost:3000.

Voice mode requires a microphone and a browser that supports the Web Speech API (Chrome, Edge, and Safari work best). Auto and manual modes work everywhere.

Other scripts

pnpm build   # production build
pnpm start   # serve the production build
pnpm lint    # lint

Browser Support

Feature Support
Auto / manual scroll, recording, all settings All modern browsers
Voice tracking Chrome, Edge, Safari (browsers exposing SpeechRecognition)

When voice tracking isn't available the app detects it and prompts you to use auto or manual mode instead.


Built with v0

This repository is linked to a v0 project — start new chats to make changes and v0 will push commits directly to this repo. Every merge to main automatically deploys.

Continue working on v0 →

Built with Anthropic Claude and v0 by Vercel, by Jordan Harrison.

About

A free, private teleprompter that follows your voice — running entirely in your browser.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages