openjutsu
One-click character swap for videos: an open-source, local Higgsfield Genjutsu alternative built on ComfyUI and MiniMax H3, with FLUX.3 Klein relighting.
Viewers vote for the next song via a mobile-friendly website.
MediaJavaScript · VueGitHub @ghormannOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Source code for our website that allows users to vote on the next song to be played.
Taken from the repository README
Loading the full README.
Read the full README ↗A real-time voting website that lets visitors choose which Christmas song plays next at a synchronized light show. Visitors cast votes from their phones, and the results are broadcast to fppscheduler via MQTT to influence the show's playlist.
Visitor Browser
│
│ HTTP / WebSocket (Hapi NES)
▼
┌─────────────┐ MQTT ┌──────────────────┐
│ Node.js │ ◄────────────────►│ MQTT Broker │
│ Hapi Server│ └──────────────────┘
│ :7654 │ │
└─────────────┘ │ /christmas/...
▲ ┌──────┴───────┐
│ Built static files │ fppscheduler │
┌─────────────┐ │ (on FPP box)│
│ Vue 3 + │ └──────────────┘
│ Vite │
│ Frontend │
└─────────────┘
frontend/) — Vue 3 SPA built with Vite. Served as static files by a web server (e.g., nginx). Communicates with the backend via REST and a persistent WebSocket subscription.server/) — Hapi.js server. Manages vote state, pushes real-time updates to all connected clients via Hapi NES WebSockets, and relays votes/status to/from an MQTT broker.nvm use 24)/christmasserver/)Copy the example config and fill in your MQTT broker details:
cp server/greglights_config_example.json server/greglights_config.json
Edit server/greglights_config.json:
{
"username": "your-mqtt-username",
"password": "your-mqtt-password",
"port": 1883,
"host": "192.168.1.x",
"send_enabled": true
}
| Field | Description |
|---|---|
host | Hostname or IP of the MQTT broker |
port | MQTT broker port (1883 for plain, 8883 for TLS) |
username | MQTT username |
password | MQTT password |
send_enabled | Set false to disable publishing votes/queues to MQTT (dry-run mode) |
Note:
greglights_config.jsoncontains credentials — do not commit it. It is listed in.gitignore.
cd server
nvm use 24
npm install
node index.js
The server listens on port 7654 by default. Override with the port environment variable:
port=8000 node index.js
cd server
docker-compose build
docker-compose up -d
The docker-compose.yml binds the server to 127.0.0.1:7654 on the host. Front it with nginx or another reverse proxy for HTTPS.
| Method | Path | Description |
|---|---|---|
GET | /queue | Returns the full data model (songs, names, snowmen, votes remaining for session) |
GET | /model | Returns the full data model (no session info) |
GET/POST | /vote/{id} | Cast a vote for song with the given ID |
DELETE | /vote/{id} | Remove a previously cast vote for song {id} |
GET/POST | /votesnowman/{id} | Vote for a snowman display |
GET | /status | Returns server health status |
GET | /faq | Returns the shared display FAQ as rendered HTML |
GET | /ws | WebSocket endpoint (Hapi NES) — subscribe to /publicData for real-time model pushes |
GET /faqThis server owns the FAQ text for both vote-now.org and thehormanns.net, so the
two sites can never drift apart. The questions and answers live in
server/data/faqs.js; anything that can change — season dates, the song count,
the address, charity names — is written there as a {{token}} and filled in
from server/data/facts.js on each request. Answers come back as ready-to-show
HTML.
| Query | Description |
|---|---|
audience | web (thehormanns.net) or app (the vote-now info page). Omit for everything. |
site | Base URL for links into thehormanns.net. Defaults to https://thehormanns.net; thehormanns.net passes an empty string so its own links stay relative. |
curl 'http://localhost:7654/faq?audience=app'
thehormanns.net calls this at build time (which is what feeds its FAQPage
structured data) and again in the browser, so edits here reach that site without
a redeploy.
Sessions are tracked server-side via a cookie (Hapi Yar). Each session gets 8 votes, replenished by 1 every 2 minutes.
The server subscribes to these topics (all under the /christmas/ prefix):
| Topic | Purpose |
|---|---|
/christmas/nameQueue | Name queue status from fppscheduler |
/christmas/scheduler/status | Scheduler run state (on/off, short list mode, debug) |
/christmas/scheduler/name_estimate | Estimated time until next name is generated |
/christmas/scheduler/all_playlist | Full song list with durations |
/christmas/snowman/status | Available snowman displays and current selection |
/christmas/falcon/player/fpp2/playlist_details | Currently playing song from FPP |
/christmas/clock | Show clock tick |
/christmas/todayPower | Power consumption stats |
/christmas/vote/stats | Aggregated vote statistics |
/christmas/vote/setShortList | Toggle short-list mode |
/christmas/vote/debug | Toggle debug mode |
The server publishes to:
| Topic | Purpose |
|---|---|
/christmas/vote/add | Notify fppscheduler of a new song vote |
/christmas/snowmanvote/add | Notify fppscheduler of a snowman vote |
/christmas/vote/songQueue | Broadcast current song queue (every 2 s) |
/christmas/vote/snowmenQueue | Broadcast current snowman queue (every 2 s) |
/christmas/personsName | Submit a visitor's name to the name display queue |
/christmas/scheduler/requestSongs | Request full playlist from fppscheduler on startup |
/christmas is hard-coded in lib/mymqtt.js.https://vote-now.org) is hard-coded in routes/index.js but only used for a redirect on a 404.mqtt:// is active.frontend/)Vite uses .env files to inject the backend URL at build time. Two files are used:
.env.local — used automatically during npm run dev (never committed):
VITE_API_BASE_URL=http://localhost:7654
VITE_WS_URL=ws://localhost:7654/ws
.env.production — used automatically during npm run build:
VITE_API_BASE_URL=https://your-domain.com/api
VITE_WS_URL=wss://your-domain.com/ws
Update .env.production to match your production server's domain before building.
| Variable | Description |
|---|---|
VITE_API_BASE_URL | Base URL for REST API calls to the Hapi server |
VITE_WS_URL | WebSocket URL for real-time Hapi NES subscription |
cd frontend
nvm use 24
npm install
npm run dev
Starts Vite dev server at http://localhost:8080 (bound to all interfaces via host: true in vite.config.js). API requests proxy to the backend via the URL in .env.local.
npm run build
Outputs static files to frontend/dist/. Copy these to your web server's document root.
| Command | Description |
|---|---|
npm run preview | Preview the production build locally |
npm run lint | Lint and auto-fix with ESLint |
npm run format | Format source files with Prettier |
npm run test | Run unit tests with Vitest |
| Route | Component | Description |
|---|---|---|
/ | HomeView | Song voting — list of available songs with vote buttons |
/names | NameQueue | Submit your name to appear on the light show name display |
/info | InfoView | Show information and FAQ |
/stats | StatsView | Voting and power usage statistics |
/other | OtherDisplaysView | Other display elements (snowmen voting, etc.) |
| Package | Purpose |
|---|---|
| Vue 3 + Vite | UI framework and build tool |
| Pinia | State management |
| Vue Router 4 | Client-side routing |
@hapi/nes | WebSocket client for real-time server pushes |
| Axios | HTTP REST client |
| Bootstrap 5 | UI styling |
Run backend and frontend in separate terminals:
# Terminal 1 — backend
cd server && nvm use 24 && node index.js
# Terminal 2 — frontend
cd frontend && nvm use 24 && npm run dev
The frontend dev server at http://localhost:8080 connects to the backend at http://localhost:7654 as configured in frontend/.env.local.
cd frontend && npm run buildfrontend/dist/ files to your web server document root./api/ and /ws to http://127.0.0.1:7654.cd server && docker-compose up -d.