An intuitive and lightweight web-based subtitle editor. Load a local video file or a YouTube link and caption it right in the browser. Open-source, fast, and easy to use. π₯βοΈ
- Two Video Sources - Edit against a local video file (all major formats) or a YouTube link
- Timeline-Based Editing - Visual timeline with drag-and-drop subtitle manipulation
- Real-time Preview - See subtitles overlaid on the video as you edit
- Undo/Redo - Full history for subtitle edits (
Cmd/Ctrl + Z/+ Shift) - Auto-Save - Automatic localStorage persistence prevents data loss
- Keyboard Shortcuts - Professional editing workflow with comprehensive shortcuts
- Dark Mode - Beautiful dark/light theme support
- Add & Edit Subtitles - Create and modify subtitle text with precise timing
- Drag & Resize - Adjust timing by dragging subtitle bars on the timeline
- In/Out Points - Set a subtitle's start (
I) or end (O) to the current playback time - Split - Split a subtitle at the playhead (
S) - Timeline Modes
- Free mode: Navigate timeline freely
- Centered mode: Playhead stays centered for easier editing
- Import/Export
- Import: SRT, VTT formats
- Export: SRT, VTT formats
- Responsive Design - Works seamlessly on desktop and tablet
- Exit Protection - Warns before leaving page to prevent accidental data loss
- Timeline Zoom - Zoom in/out for precise or overview editing
- Visual Feedback - Dimmed past timeline for better focus
Try it live at: captiony.vercel.app
- Node.js 18.17.0 or later
- npm, yarn, pnpm, or bun
- Clone the repository
git clone https://github.com/zeikar/captiony.git
cd captiony- Install dependencies
npm install
# or
yarn install
# or
pnpm install
# or
bun install- Run the development server
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev- Open http://localhost:3000 in your browser
- Load Video: Pick a local video file (drag & drop or click) or paste a YouTube link from the empty-state picker. Use "Select Video" in the toolbar to switch sources later.
- Add Subtitles: Press
N(orCmd/Ctrl + Enter) to add a subtitle at the current time - Edit Timing: Drag subtitle bars on the timeline, set In/Out points with
I/O, or edit timestamps directly - Edit Text: Click on subtitle text to edit in the right panel
- Export: Download your subtitles as SRT or VTT files
Playback
Space/K- Play/Pauseβ/β- Jump back / forward 5 secondsJ/L- Jump back / forward 1 second
Subtitles
N/Cmd/Ctrl + Enter- Add new subtitle at current timeβ/β- Select previous / next subtitleM- Jump to nearest subtitleEnter- Edit selected subtitleDelete/Backspace- Delete selected subtitleEsc- Deselect subtitleI/O- Set In / Out point to current timeS- Split subtitle at current timeShift + β/Shift + β- Nudge selected subtitle left / right (0.1s)
History
Cmd/Ctrl + Z- UndoCmd/Ctrl + Shift + Z- Redo
Click Shortcuts in the toolbar for the full list in-app.
app/ - Next.js App Router pages
page.tsx - Main application page (NavBar + CaptionEditor)
layout.tsx - Root layout with theme + notification providers
api/auth/ - Firebase session-cookie auth routes (scaffolding)
components/
editor/ - Subtitle editor
CaptionEditor.tsx - Main editor container (video + editor + timeline)
VideoPlayer.tsx - Video player with per-source surfaces
SubtitleTimeline.tsx - Timeline visualization
SubtitleEditor.tsx - Subtitle list + text editor panel
ToolBar.tsx - Import/export + source-switch toolbar
components/ - Granular UI pieces
LocalVideoSurface.tsx - <video> surface (local files)
YouTubeSurface.tsx - react-player surface (YouTube)
VideoUploader.tsx - Empty-state picker (file | YouTube)
SubtitleBar.tsx, TimelineGrid.tsx, TimelinePlayhead.tsx, ...
hooks/ - Keyboard, drag, timeline, and player hooks
utils/ - Time / subtitle / timeline / video helpers
layout/NavBar.tsx - Top navigation bar
ui/ - Reusable UI components (e.g. DarkModeToggle)
contexts/ - React Context (toast notifications)
lib/
stores/ - Zustand state management
subtitle-store.ts - Subtitle data, SRT/VTT I/O, undo/redo (zundo)
video-store.ts - Playback state (local + YouTube sources)
firebase/ - Web + Admin SDK auth helpers (scaffolding)
metadata.ts - SEO metadata configuration
public/ - Static assets
- Framework: Next.js 15 (App Router) + React 19
- Language: TypeScript
- Styling: Tailwind CSS 4
- State Management: Zustand (localStorage persistence) + zundo (undo/redo)
- Video Playback: HTML5
<video>(local) + react-player 3.x (YouTube) - UI Components: Headless UI, Heroicons, react-virtuoso (virtualized list)
- File Handling: FileSaver.js
- Testing: Vitest + React Testing Library
- Analytics: Vercel Analytics
Subtitles are automatically saved to browser localStorage as you work. When you return to the app, your work is automatically restored, preventing data loss from accidental page closures or browser crashes.
The timeline provides a visual representation of your subtitles over time. You can:
- Drag subtitle bars to change their position
- Resize bars by dragging edges to adjust duration
- Zoom in for frame-accurate editing
- Zoom out for an overview of your entire subtitle track
- Import: Load existing SRT or VTT subtitle files to continue editing
- Export: Download your subtitles in industry-standard SRT or VTT formats compatible with YouTube, video players, and professional editing software
Captiony works best in modern browsers:
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your 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
This project is licensed under the MIT License - see the LICENSE file for details.
- Next.js - The React framework
- Tailwind CSS - Utility-first CSS framework
- Zustand - State management
- Heroicons - Beautiful icons
Built with β€οΈ by zeikar
