Executive summary
PromptSync is a small browser-based teleprompter and presentation viewer.
The core workflow is simple: open the controller on a laptop or modern browser, open the viewer on another device, enter the same six-digit room code, and the viewer stays synchronized with the controller. The controller pushes script content, scroll position, autoplay state, and display settings over WebSockets.
The project is built with plain browser technologies, Node.js, SCSS, and the ws WebSocket package. The README explicitly calls out broad browser compatibility, including older tablets, as a design goal.
The problem
A teleprompter sounds simple until the display and the controls are on different devices.
If the script is on the viewer device, editing text or changing scroll speed means touching the same screen you are trying to read from. If the screen is an old iPad or tablet, many modern web apps are too heavy, too framework-dependent, or awkward around fullscreen behavior. On iOS Safari, fullscreen also has limitations, so the viewer flow needs to account for Home Screen installation.
PromptSync solves the small but real friction of using a second device as a clean reading screen while keeping control on the main device.
The thesis
The bet is that this does not need to be a native app.
A controller/viewer split, six-digit room codes, and a lightweight WebSocket state layer are enough for the main teleprompter workflow. The controller owns the script, scroll, autoplay, and display controls. The viewer only needs to join a room and render the latest synchronized state.
The implementation intentionally keeps the frontend simple: static HTML pages, one browser script, SCSS compiled to CSS, and a Node.js server that serves files and manages WebSocket rooms.
What I built
Controller and viewer roles
PromptSync has two separate browser entry points:
controller.htmlviewer.html
The controller can generate or join a six-digit room, write the script, push updates, control scroll, start or pause autoplay, adjust autoplay speed, open a viewer window, and preview the rendered output.
The viewer joins the same room code and displays the synchronized content.
Real-time room synchronization
The server keeps in-memory room state for each six-digit code.
Each room stores:
- current script content
- scroll percentage
- autoplay state and speed
- display settings
- controller connection
- viewer connections
When the controller changes content, scroll, autoplay, or settings, the server broadcasts the updated state to connected viewers.
There is no documented persistent database. Room state is in memory, and empty rooms are deleted when the controller and viewers disconnect.
Teleprompter controls
The controller supports:
- live script editing
- manual push update
- scroll synchronization
- scroll up and scroll down buttons
- autoplay start and pause
- autoplay speed control
- font size control
- line height control
- content width control
- top and bottom frame insets
- horizontal offset
- dark and light theme
- horizontal mirroring
- vertical mirroring
- presentation mode
- fullscreen preview
The viewer supports room joining and fullscreen-style viewing.
Lightweight markdown-style rendering
PromptSync does not use a full markdown engine.
The browser script includes a small renderer for the formatting needed by a teleprompter:
# Heading 1## Heading 2### Heading 3- paragraphs
**bold***italic*- bullet lists
---separators[pause]blocks
That keeps the renderer small and predictable for older browser support.
Older iPad support
The README specifically documents a recommended setup for iPad 4 / iOS 10.
The viewer is intended to be added to the iPad Home Screen and launched from that icon, because iOS Safari needs that flow for fullscreen-like behavior. If the viewer is opened inside normal Safari on iOS, the app shows instructions to use Share → Add to Home Screen.
The browser script also includes iOS Safari detection, viewport handling, orientation handling, and an immersive viewer mode fallback.
Deployment
The repository includes Docker production support.
The Dockerfile uses Node 22 Alpine, builds SCSS into plain CSS, copies the static public files and server into a runtime image, exposes port 3000, and defines a healthcheck against /health.
The Docker Compose file runs the app as a container named promptsync, with BASE_PATH support and a healthcheck.
The README documents a production topology with:
- app path:
/root/promptsync - public URL:
https://promptsync.site - nginx ingress
- internal Docker networking
- Cloudflare DNS-only routing
- direct health checks
The GitHub Actions workflow builds CSS, verifies public/styles.css, then deploys over SSH by pulling main on the server and recreating the Docker Compose service.
Architecture
Controller browser
→ controller.html
→ app.js
→ WebSocket
Node.js server
→ static file serving
→ in-memory room state
→ WebSocket broadcast
Viewer browser
→ viewer.html
→ app.js
→ synchronized script, scroll, autoplay, and display settings
Current status
Active with public demo. Production deployment runs at promptsync.site via Docker, nginx, and GitHub Actions deploy on push to main. Controller and viewer flows, WebSocket room sync, and older iPad Home Screen setup are documented and working.
No installer package, public user count, or revenue model. Room state is in-memory only — rooms disappear when all clients disconnect.