尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A kiosk website for collecting student feedback with hand gestures. You keep a list of questions on the admin page and assign each one to one or more variants. The kiosk runs one variant at a time and asks that variant's questions in random order. Until someone waves at it, the kiosk shows an idle screen. There are three variants:
Hand tracking runs in the browser with MediaPipe Gesture Recognizer. Video never leaves the device. Only the answer is sent to the server.
Requires Node.js 22.13 or newer and Docker (OrbStack or Docker Desktop) for the Postgres database.
docker compose up -d
npm install
ADMIN_PASSWORD=choose-a-password npm start
If ADMIN_PASSWORD isn't set, the password is admin. Set a real one before using it with students.
Questions and answers are stored in Postgres 17, which docker-compose.yml runs on 127.0.0.1:5434 with its data in the pgdata Docker volume. The server creates its tables on start. Set DATABASE_URL to use another database (default postgres://feedback:feedback@localhost:5434/feedback).
questions: the text and the variants it is assigned to (variants, an array).answers: one row per answer, with the question, the variant it was given in, and answer (agree/disagree, thumbs) or rating (1–5, stars and fingers). Deleting a question deletes its answers.settings: the variant and camera the kiosk uses.To start over, run docker compose down -v, which also deletes the volume.
Earlier versions stored everything in data/feedback.db. To copy that into Postgres once:
npm run import-sqlite
Statements with the same text become one question, assigned to every variant they were asked in, and their votes and ratings become that question's answers with the original timestamps. The import only runs into an empty database and never changes the SQLite file. Set DB_PATH to import a different file.
WAVE_MAX_TILT), and a row of dots fills as it does. A swing must move the hand sideways at least 0.9 palm length (WAVE_SWING), so it works at any distance from the camera, and further sideways than up or down. It must take at least 0.1 seconds (WAVE_MIN_SWING_MS), so a jump in the tracking doesn't count, and the next swing must follow within 0.8 seconds (WAVE_GAP_MS). Three swings (WAVE_SWINGS), back, forth and back again, wake the kiosk: about a second of normal waving. Anything else, like raising the hand into view or moving it about, starts the count over. If no question is assigned to the active variant, the idle screen says so and ignores waves.
Which hand is followed. The model looks for up to three hands (MAX_HANDS), and the kiosk follows the raised one: the hand highest in the picture. A hand that points, waves or makes a gesture is held up, so a hand resting at the bottom of the picture never takes over. A new hand less than a third of the size of the biggest one (HAND_MIN_SIZE) belongs to someone further away and is ignored, but the hand being followed is kept whatever its size, for example when a resting hand is much closer to a laptop camera. Another hand only takes over when it is at least a palm higher than the followed hand (HAND_SWITCH), so two hands at about the same height don't flicker. A hand moving fast blurs and can be lost for a few frames: for 0.4 seconds (HAND_LOCK_MS) the kiosk keeps its square in place and waits for it, instead of jumping to a lower hand. A hand of about the same size that turns up at the same height or higher counts as the same hand, however far to the side, because a waving hand that blurs mid-swing comes back at the other end of the swing.IDLE_AFTER_MS), the kiosk goes back to the idle screen.MIN_SCORE), a large ring fills. If the student switches thumbs, it starts over. If they drop the gesture, it resets.Which question is shown. The kiosk asks the questions assigned to the active variant one at a time, in random order. Every question is asked once before any is repeated, and the same question never comes twice in a row. Questions added on the admin page join the current round. If no question is assigned to the active variant, the kiosk says so and doesn't accept answers. It checks for a new variant and for changed questions every few seconds (POLL_MS).
The timings, the star hit area, the cursor smoothing, the finger-count thresholds (FINGER_RATIO, THUMB_RATIO) and the wave detection are defined at the top of public/kiosk.js. Each variant has its own hold time: THUMBS_HOLD_MS (3 s), STARS_HOLD_MS (1 s) and FINGERS_HOLD_MS (3 s).
Variants are thumbs, stars and fingers.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/kiosk | – | {variant, camera, questions: [{id, text}]}: the kiosk settings and the questions assigned to the active variant |
| POST | /api/answers | – | {questionId, variant, answer: "agree" | "disagree"} for thumbs, or {questionId, variant, rating: 1–5} for stars and fingers. Returns the question's updated results for that variant |
| GET | /api/settings | – | {variant, camera}: the variant and camera the kiosk uses. camera is macbook or logitech |
| PATCH | /api/settings | admin | {variant?, camera?} changes either or both |
| GET | /api/questions | admin | All questions, newest first, each with results per variant |
| POST | /api/questions | admin | {text, variants} adds a question |
| PATCH | /api/questions/:id | admin | {text?, variants?} changes a question |
| DELETE | /api/questions/:id | admin | Deletes a question and its answers |
| GET | /api/questions/:id/answers | admin | Every answer to a question, newest first |
| GET | /api/export.csv | admin | All answers as CSV |
Admin requests send the password in the x-admin-password header.
You pick the camera on the admin page. The kiosk switches within a few seconds, without a reload. Each camera is defined in CAMERAS at the top of public/kiosk.js:
| Camera | Found by name | Picture |
|---|---|---|
| MacBook camera | "FaceTime", "MacBook" or "Built-in" | Landscape (up to 1920×1080), as the sensor delivers it. For testing on a laptop. |
| Logitech StreamCam | "StreamCam", then "Logitech" | Portrait 1080×1920 for the vertical display. Mount the camera upright: the kiosk rotates its picture 270°. |
The picture is always mirrored, so it feels like a mirror. If the chosen camera isn't connected, the kiosk uses the browser's default camera and logs a warning in the console. To correct the orientation of a camera, change its rotation (clockwise, in degrees).
Browsers only allow camera access on localhost or HTTPS. Running the server and kiosk on the same machine works as is. If the kiosk runs on a different device, such as a tablet, serve the site over HTTPS, for example:
DATABASE_URL.npx localtunnel --port 3000, or use cloudflared tunnel --url http://localhost:3000.--kiosk flag also works.STABLE_FRAMES), and changing the count restarts the timer. A fist (0 fingers) isn't an answer.