Movie script table reads, without the table.
Scripta is a full-stack web application that turns screenplay table reads into an asynchronous workflow. A screenwriter uploads a script, assigns characters to actors, and shares unique recording links. Each actor records their lines remotely — on their own time, from their own device. Scripta then stitches every recording into a single, cohesive table read with natural pauses and subtle room tone — all entirely in the browser.
No scheduling conflicts. No Zoom calls. No expensive studio time.
- Full-screen 3D typewriter scene built with React Three Fiber and Three.js
- Type directly onto the paper — each keystroke plays a realistic typewriter sound effect
- Cinematic dark theme with film grain overlay, vignette, and gold accents
- Carriage return sound plays when the script is parsed
- Paste or type a script in standard
CHARACTER: dialogueformat - Upload a
.txtfile to import scripts instantly - Load a sample script (Jack & Rose demo) to try it out
- Automatic extraction of unique characters and ordered dialogue lines
- Map each character to a real person (actor name)
- Each actor receives a unique shareable link — no accounts needed
- Token-based auth: writers get a
writer_token, actors get ashare_token
- Actors open their personal link and see the full script for context
- Their assigned lines are highlighted; other characters' lines are shown for reference
- One-click recording via the browser microphone (MediaRecorder API)
- Playback, re-record, and upload controls for each line
- Real-time progress tracking
- Between an actor's lines, the app narrates other characters' dialogue aloud using the Web Speech Synthesis API
- Gender-aware voice selection — infers male/female from the character name (70+ common names + heuristic fallback)
- Prefers high-quality Siri voices on macOS (Samantha, Daniel, Karen, Alex)
- Separate pitch and rate tuning per gender for natural feel
- Penalizes robotic/novelty voices automatically
- Zero server-side processing — everything runs in the browser via the Web Audio API
- Loads all recorded clips from Supabase Storage
- Converts to mono, resamples to 44.1 kHz
- Concatenates in script order with configurable pauses (600ms default)
- Overlays subtle pink noise room tone (Voss-McCartney algorithm) for studio ambience
- Exports to WAV with proper RIFF headers
- Instant playback + download from the dashboard
- Track recording progress per actor with visual progress bars
- "Lines Recorded — X of Y" label for clarity
- Copy actor invite links with one click
- Generate the final table read and listen/download immediately
- Duration display and download button appear as soon as generation completes
src/
├── App.tsx # Router (5 routes)
├── pages/
│ ├── create.tsx # 3D typewriter + script input + parsing
│ ├── WriterPage.tsx # Alternative 3-step wizard flow
│ ├── DashboardPage.tsx # Writer dashboard — progress, generate audio
│ ├── ActorPage.tsx # Actor recording page — mic, TTS, upload
│ ├── Index.tsx # Landing page
│ └── NotFound.tsx # 404
├── components/
│ ├── TypewriterScene.tsx # R3F Canvas — 3D typewriter + paper overlay
│ ├── ScriptInput.tsx # Script textarea + sample loader
│ ├── ParsedLinesTable.tsx # Parsed lines review table
│ ├── CharacterActorAssignment.tsx # Character → actor mapping UI
│ ├── ProgressBar.tsx # Visual progress bar
│ ├── LineRecorder.tsx # Per-line recording controls
│ ├── Logo.tsx # Brand logo
│ └── ui/ # shadcn/ui primitives
├── hooks/
│ └── useAudioRecorder.ts # MediaRecorder hook
├── lib/
│ ├── types.ts # Core types (Session, Actor, Line, etc.)
│ ├── scriptParser.ts # CHARACTER: dialogue parser
│ ├── audioStitcher.ts # Client-side audio stitching engine
│ └── utils.ts # cn() classname helper
├── integrations/
│ └── supabase/
│ ├── client.ts # Supabase client init
│ └── types.ts # Auto-generated DB types
└── public/
├── models/ # 3D typewriter GLTF model
└── sfx/ # Typewriter sound effects (keys + return)
Three Postgres tables managed by Supabase:
| Column | Type | Description |
|---|---|---|
id |
uuid (PK) | Auto-generated |
title |
text | e.g. "Table Read — 2/7/2026" |
script_text |
text | Full raw script |
status |
text | draft → recording → generated |
writer_token |
text | Auto-generated, used in dashboard URL |
result_file_path |
text | Path to final stitched audio |
created_at |
timestamp | Auto |
| Column | Type | Description |
|---|---|---|
id |
uuid (PK) | |
session_id |
uuid (FK → sessions) | |
name |
text | Actor's real name |
character_name |
text | Character they play |
share_token |
text | Auto-generated, used in actor recording URL |
| Column | Type | Description |
|---|---|---|
id |
uuid (PK) | |
session_id |
uuid (FK → sessions) | |
actor_id |
uuid (FK → actors) | |
line_index |
int | Order in script |
character_name |
text | |
dialogue_text |
text | |
audio_file_path |
text | Path in Supabase Storage bucket |
audio_uploaded_at |
timestamp |
Storage: A recordings bucket holds uploaded audio files (.webm / .mp4).
┌─────────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐
│ 1. Writer │────▶│ 2. Parse & │────▶│ 3. Actors │────▶│ 4. Generate │
│ uploads │ │ assign │ │ record lines │ │ table read │
│ script │ │ actors │ │ remotely │ │ (WAV) │
└─────────────┘ └──────────────┘ └────────────────┘ └──────────────┘
- Writer pastes/uploads a script on the typewriter page → clicks Parse Script
- Reviews parsed lines → assigns actor names to characters → Create Session
- Copies unique links and sends them to actors
- Actors open their link → see the full script → record their lines one-by-one with TTS narration for context
- Writer returns to the dashboard → sees progress → clicks Generate Audio
- Audio stitcher runs in-browser → final WAV is ready to play and download
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | React 18 + TypeScript | UI framework |
| Build | Vite 5 (SWC) | Fast dev server & builds |
| 3D | Three.js + React Three Fiber + Drei | Typewriter scene |
| Styling | TailwindCSS + shadcn/ui (Radix) | Components & design system |
| Backend | Supabase | Postgres DB + file storage + auto tokens |
| Recording | MediaRecorder API | Browser mic capture |
| Stitching | Web Audio API | Client-side audio processing |
| TTS | Web Speech Synthesis API | Gender-aware narration |
| Icons | Lucide React | UI icons |
| Toasts | Sonner | Notifications |
| Testing | Vitest + Testing Library | Unit tests |
- Node.js 18+
- npm or yarn
- A Supabase project (free tier works)
git clone [https://github.com/karimmufti/Scripta.git](https://github.com/karimmufti/Scripta.git)
cd Scriptanpm installCreate a .env file in the project root:
VITE_SUPABASE_URL=[https://your-project.supabase.co](https://your-project.supabase.co)
VITE_SUPABASE_PUBLISHABLE_KEY=your_supabase_anon_keyIn your Supabase dashboard:
- Create the tables (
sessions,actors,lines) with the schema described above - Create a storage bucket called
recordingswith public access enabled - Enable RLS policies as needed (or disable for development)
npm run devThe app will be available at http://localhost:5173.
npm run build # Production build
npm run preview # Preview production build
npm run lint # ESLint
npm run test # Run tests (Vitest)
npm run test:watch # Watch mode tests- Cinematic dark theme — deep radial gradient background
- Film grain overlay — subtle texture with overlay blend mode
- Vignette — radial gradient darkening at edges
- Gold accent —
#F5C542for branding, headings, and CTAs - Glassmorphism — panels with
backdrop-filter: blur(16px)and semi-transparent backgrounds - Pill-shaped buttons — with hover glow effects and smooth transitions
Scripts should follow standard screenplay dialogue format:
CHARACTER_NAME: Line of dialogue here.
ANOTHER_CHARACTER: Their response goes here.
CHARACTER_NAME: And so on...
- One line per dialogue entry
- Character name before the colon (auto-uppercased)
- Blank lines and lines without colons are ignored
- Fork the repo
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Built with ☕ and 🎬 at SparkHacks 2026