OBS browser overlay for shared text and image elements.
The app uses a custom Node HTTP server so Next.js and Socket.IO share the same port, stores overlay items in Postgres through Prisma, and keeps connected clients in sync in real time.
- Create draggable text items.
- Create draggable image items from uploaded files.
- Move and resize items, then persist those changes.
- Toggle item visibility so only visible items render on the overlay page.
- Broadcast create, update, visibility, and delete events with Socket.IO.
- Next.js 16 App Router
- React 19
- Socket.IO
- Prisma + Postgres
- Tailwind CSS v4
- Node.js 20 or newer. The Docker image uses Node 24.
- Postgres, either local or via Docker.
Create a .env file in the project root.
POSTGRES_DB=overlay
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
DATABASE_URL="postgresql://postgres:password@db:5432/overlay?schema=public"
TTV_CHANNEL=your_twitch_username
DOMAIN_NAME=your.overlay.url.comInstall dependencies:
npm installGenerate the Prisma client. This project outputs it to app/generated/prisma:
npx prisma generateApply migrations to your development database:
npx prisma migrate devStart the custom dev server:
npm run devOpen:
- Editor: http://localhost:3000
- Overlay: http://localhost:3000/overlay
Notes:
- The custom server listens on port
3000inserver.ts. - Use the
Addbutton to create text or upload an image.
The included compose file starts the app, Postgres, and Adminer.
Bring everything up:
docker compose up --build -dOpen:
- App: http://localhost:3001
- Adminer: http://localhost:8081
The container entrypoint runs Prisma migrations on startup.
Add a Browser Source that points at the overlay page:
- Local:
http://localhost:3000/overlay - Docker:
http://localhost:3001/overlay
The UI reads and writes through the routes under app/api.
GET /apireturns all items.GET /api?hidden=falsereturns visible items only.POST /apicreates an item.- Text payload:
{ type: "text", x, y, text, fontSize } - Image payload:
{ type: "file", x, y, path, width, height }
- Text payload:
PATCH /api/:idupdates coordinates and eitherfontSizefor text orwidth/heightfor images.PATCH /api/:id/visibilitytoggles thehiddenflag.DELETE /api/:iddeletes the item.
Socket.IO broadcasts these events to other connected clients:
item:createitem:updateitem:visibilityitem:delete
The overlay page subscribes to the same events and only renders items where hidden is false.
Prisma models live in prisma/schema.prisma:
Itemstorestype,x,y, andhidden.TextItemstorestextandfontSize.FileItemstorespath,width, andheight.
- Uploaded images are converted to Data URLs and saved in Postgres. That is convenient for prototyping, but large images can grow the database quickly.
- There is no authentication or authorization, so anyone who can reach the app can edit the overlay.