# Game Buzzer Version 1.3.0 · PHP 8.1+ · No database, npm build, external service, or student accounts. Target URL after deployment: https://jasr.me/github/game-buzzer/ Two games for six teams: 6th-grade boys/girls, 7th-grade boys/girls, and 8th-grade boys/girls. Designed for 5–10 students per team, one or more leader phones per team, and two projectors showing the same display link. ## Leader question poll Share **leaders.php** with leaders before game day. The 100-question bank and leader suggestions appear in a random order for each page visit, stable while browsing and saving. Voting shows no popularity counts, rankings, or activity totals. Each browser's latest saved picks count once per question. Existing picks and suggestions are preserved. A View results link opens the separate results page; save your picks before leaving. Open **leader-results.php** separately (or use **view question rankings** on the home page) for public combined totals, rankings, anonymous activity, and **Copy top 10 for game setup**. Results update every 10 seconds; equal totals share a rank. Paste the copied questions into the game setup editor. Results are public, not access restricted; share the voting link with voters. Anonymous browser IDs and activity review flags work as before. ## First-time host setup through user-management 1. Deploy this folder alongside the existing user-management and 1-Framework folders. Shared identity must already be configured and available over HTTPS. 2. Open Game Buzzer and choose **Open user-management**. Sign in as the existing **jasrasr** Super Admin, completing any required password change. Return to the game tab and tap **Check sign-in**. 3. Choose **Enable Game Buzzer**. Only the active, non-demo jasrasr account with central directory Super Admin rights can perform this step. No account is created, promoted, reset, or renamed. 4. Setup registers the game-buzzer project at its actual deployment path without overwriting an existing conflicting registration. It creates storage and a private, PHP-guarded identity-enabled.php marker. Repeated setup is safe. No separate host password or manual config file is required. 5. Create a game. Shared login and CSRF protection authorize creation; the old creation-password route is disabled once shared login is enabled. Existing non-demo users with explicit Game Buzzer admin access (or existing all-project admin scope) can create games. Setup itself remains restricted to jasrasr. 6. Save the private host link. Share each team link only with its leaders; share the survey link with students. Open the projector link on both screens. Guests do not need accounts. Room host links remain bearer capabilities, including after central logout or revocation; central revocation stops NEW room creation, not already-issued room invitations. 7. Click **Enable chime** on ONE projector/host browser and **Full screen** if desired. Existing game files and private config files are preserved. Setup does not provision any central accounts or automatically grant users higher permissions. New game registration honors the directory’s existing all-project scope. If setup cannot finish after registration (for example, storage is not writable), correct the reported issue and retry; registration and marker creation are idempotent. No public first-visitor account-claim flow exists. The shared cookie must cover the Game Buzzer URL (the default cookie_path is /). If sign-in succeeds but the game still shows signed out, check cookie scope, hostname, HTTPS and the configured identity portal URL. If jasrasr is missing or lacks the required rights, use an existing central directory administrator to review the account; this application will not elevate it automatically. ### Optional existing/manual configuration Existing password-configured installations continue working until an owner explicitly enables shared login. config.local.php stays private and untouched. It can still set data_dir to a PHP-writable directory outside public_html. The fallback data/ folder has an Apache/LiteSpeed deny rule; use equivalent denial on other servers. Both runtime data and local config are ignored by Git. Preserve ignored data during deployment. ## Standalone buzzer game - Each leader opens their team invitation on their phone. Up to 12 phones may join each team; reloading retains the phone identity. - The host chooses 10, 15, 30, or 60 seconds and starts a round. A three-second countdown allows connected clients to receive the opening time. - Press the giant button when GO appears. The first accepted press per team counts. Additional phones on the same team cannot create extra places or overwrite its time. - All six teams may buzz; the projector shows their order and milliseconds behind first. Presses before opening, after closing, or from an older round are rejected. - Close the buzzer to finalize the order and automatically award **60, 50, 40, 30, 20, and 10 points** by buzz order. Every team that buzzes earns at least 10 points. The scoreboard’s −/+ controls allow deliberate corrections or custom scoring. - Start a fresh round to clear the order while retaining scores. Majority Rules scores separately when the host reveals an answer. - On the host desk, **Next** advances the normal sequence: close the arrival survey, start a buzzer round, end it and open the next question, reveal and score, then start the next buzzer round. Manual controls remain available under **Manual controls and overrides**. ## Host rehearsal and score reset All controls are on the private host desk: - **Start practice mode** lets team phones buzz without scoring. Score adjustments are disabled; the scoreboard stays unchanged. End an unfinished live buzzer round before switching modes. - **Try demo voted questions** starts/restarts practice with 20 sample A/B votes per question, including one tie. Use Next to reveal, then run buzzer rounds and the following questions. Sample totals never enter student ballots; the real arrival survey continues to accept answers while open. - **Exit practice mode** discards practice predictions/reveals and restores your real question progress. Old buzzer timers and presses are cleared; start a new live round. All phones and projectors show a practice label while rehearsing. - **Reset all scores** asks for confirmation and zeros all six team scores. It keeps survey answers, questions, phones and completed scoring records; re-revealing a completed question does not re-award points. Future rounds continue to score normally. Reset is available during practice too. - **Load top 10 favorite questions** takes the combined favorites ranking, including custom suggestions, and loads ten shuffled arrival questions. Ties use the same stable order as results; if fewer than ten questions have votes, zero-vote questions fill the remaining slots. This works before real voting or play starts. You can also use **Use top 10 favorite questions** on the create-game screen and review/edit them before creating a room. Changing questions requires a new game once voting/play has started. ## Arrival survey → Majority Rules (under 15 minutes) Each new game shuffles the ten questions once. Every student and kiosk sees that same randomized order, which is also used for the host’s question reveals. 1. While students arrive, have each answer all ten A/B questions privately on the survey link. No name, grade, phone number, or email is collected. Alternatively, use the **Check-in kiosk** link on a shared tablet; the Next student button clears the form and issues a fresh ballot identity. 2. Supervise one submission per student. The normal flow locks one ballot per browser token; it is not identity verification and cannot prevent someone using another browser or clearing storage. Kiosk mode intentionally supports multiple students. 3. Close the arrival survey once everyone has answered. It cannot reopen, so later questions always use the same group’s locked answers. 4. After the host closes the arrival survey, teams discuss each question’s likely majority. After a buzzer round closes, the next unrevealed question opens automatically. A leader taps **Predict A** or **Predict B**. The first submitted prediction from either of a team’s phones locks that team’s answer. Other teams’ predictions and all vote totals remain hidden until reveal. 5. The host taps **Next** to reveal and score: matching the room’s majority earns +30 points. An exact tie awards +30 points to each team that submitted. Teams without a prediction receive zero. Reveal is safe to repeat without adding points again. 6. Tap **Next** to start the following buzzer round and repeat. Select a question manually under **Manual controls and overrides** to interrupt the sequence. Budget about 60 seconds per question, plus instructions and the winner announcement. Both games share the scoreboard. Create separate rooms to keep scores separate. Survey responses are stored only as anonymous arrays of A/B choices associated with random browser tokens. Public responses expose only total ballot count and the current question’s aggregate totals after reveal. Browser local storage contains capability links and random identifiers; use trusted host devices. ## Timing and latency: what this does and does not promise The authority is the order PHP accepts requests under an exclusive per-room file lock. Each accepted press is stamped with the server clock inside that lock. This is **server acceptance order**, not a claim about which finger physically touched glass first or which packet reached the network card first. Browser timestamps are never trusted for ranking. Different Wi-Fi paths, cellular links, browser scheduling and server queues can change a close result. Presses POST immediately on pointer-down. Polling (700 ms between completed requests, slower for hidden tabs) only refreshes screens; a press never waits for a poll. The displayed countdown uses an approximate server clock offset. It is informational, and the server enforces opening/closing. Measured request round-trip time is shown; it is not subtracted from scores or used to guess a one-way delay. A stale connection disables the phone’s controls until polling recovers. Host responses cannot be overwritten by an older in-flight state poll. Finishers within 150 ms of first place are labeled CLOSE FINISH as a host cue. That is a convenience threshold, **not** a measured confidence interval or a promise that larger gaps are fair. Use the same reliable Wi-Fi, keep leader phones awake, avoid VPNs/cellular mixing, try a practice round with the actual phones, and replay disputed rounds. Maintain the same number of leader phones per team when possible. This PHP version avoids assuming that shared hosting can run a persistent WebSocket service. WebSockets or a local server could reduce some overhead but cannot guarantee physical press ordering. For competition-grade precision, use dedicated hardware. This app is intended for friendly group games. ## Storage and operations - Rooms expire after 24 hours. Expiration blocks access but does not delete files; periodically remove only expired 12-hex-character room `.php` and `.lock.php` files (never delete `identity-enabled.php`, `leader-questions.json`, or `leader-questions.lock`) from the configured data directory when games are no longer active. No automatic data-deleting migration runs. - One room uses a stable lock file and atomic replace of its JSON-backed PHP data file. Shared storage must support reliable `flock` and same-directory rename. A single PHP host with local disk is the intended deployment. - Room IDs, host capabilities, and team invitations are randomly generated. Host mutations require the host secret; phone mutations require a registered device token. Requests require JSON; no permissive CORS or cookie authentication is used. - A room supports 300 ballots, 12 phones per team, and 3,000 host operations. It is a small-group app, not a public high-traffic service. Protect shared admin accounts (or the legacy creation password) and apply host-level request limiting for an internet-facing installation. - Do not open multiple host controllers and operate them simultaneously. Use one host and any number of projector displays. - No remote fonts, analytics, QR service, or CDN requests are needed. Team QR codes and printable sheets are built into the host desk. ## Verification ```sh php game-buzzer/tests/game.test.php php game-buzzer/tests/host-tools.test.php php game-buzzer/tests/concurrency.test.php node --check game-buzzer/app.js node --test game-buzzer/tests/identity.test.cjs ``` Tests cover authorization, early/late/stale presses, two phones per team, hidden results, locked surveys and predictions, majority/tie scoring, repeated awards, idempotent host operations and file persistence. The concurrency test starts 12 independent PHP processes and checks that exactly six team positions survive. CI also checks PHP/JavaScript syntax. Test on the actual venue Wi-Fi before using it with the group; local tests cannot establish Hostinger/venue latency. ## Release identification Every page displays the shared revision and source update timestamp in its footer (Eastern time). Update `GB_REVISION` and `GB_UPDATED_AT` in `release.php` for every release; this is not a deployment timestamp. Voting is `leaders.php`; public results are `leader-results.php`. Results show question IDs separately from rank, explicitly label ties, and mark zero-vote questions as not yet picked. Submitting a custom question automatically saves it as a favorite for the submitting browser and shares it with others. Existing saved favorites stay intact. Other unsaved checkbox changes still require **Save my picks**. The author can uncheck the suggestion and save to remove their pick. Custom questions are numbered 101, 102, and onward in submission order, including existing suggestions. Their numbers stay the same as popularity and voting order change. For a shared phone, tap **Next voter / clear selections** after choosing questions. It saves the current ballot, generates a new anonymous voter ID, clears selections and search, and reshuffles for the next person. Previous votes remain counted. A failed save keeps the current voter and selections intact. ## Team invitation QR codes Create a game, then open **Team phone invitations and QR codes** in the private host controls. Each team has a labeled code, **Download QR** PNG button, and copyable invitation. Multiple phones scan the same team code; the first phone from that team to buzz counts. QR generation runs locally using the vendored MIT-licensed qrcode-generator by Kazuhiko Arase. Team keys are included in the QR; share each code only with its team. The private host invitation is never turned into a QR code. Use **Print all six QR codes** for a two-column, three-row sheet with team labels and spacing. **Print this team** prints one large code on its own page. Printouts include only the game title, team labels, codes and scan instructions. Choose portrait paper and 100% scale; printer settings may affect pagination. **Save my picks** now completes the current ballot and reloads with a fresh voter ID, no selections, and shuffled questions. **Next voter / clear selections** does the same. Empty ballots are not saved. Returning to edit a completed ballot is not supported by this shared-device flow; repeated saves before this change did not multiply question votes.