A small real-time Jeopardy-style game built with Vue 3 + Vite on the frontend and AWS Amplify (AppSync + Lambda + DynamoDB) on the backend.
Jeop-Aarathi is a TV-friendly, moderator-driven Jeopardy-style game for a single party or event. It focuses on:
- High-contrast visual display for a room or projector.
- Smooth, keyboard/mouse-driven control by the host.
- Real-time buzzer experience for players on their phones.
- Simple deployment via AWS Amplify with a single shared game state.
Questions are stored in DynamoDB and loaded via a GraphQL API; they are not checked into git.
-
Two-team scoreboard
- “Autumn Orange” and “Sea Foam Green” teams.
- Automatic score updates based on question values and answer correctness.
- Negative scores are clearly displayed in red.
- Direct inline editing: click a team’s score to correct it mid-game.
-
Jeopardy-style game board
- 5×5 grid: 5 categories × values
200, 400, 600, 800, 1000. - Categories and questions are loaded from the backend via GraphQL and cached in the browser.
- Value cells become disabled/“answered” after use, based on backend
usedflags. - After a host reload mid-game, already-used clues remain greyed out and cannot be clicked again.
- 5×5 grid: 5 categories × values
-
Question modal & scoring flow
- Full-screen modal that does not obscure the scoreboard at the top of the screen.
- Phases:
- Reading / buzz prep: clue is shown while buzzers are temporarily disabled. The host sees a configurable pre-buzz countdown bar ("Buzzers active in N…") before buzzers open.
- First attempt: one team is locked in; host scores Correct or Wrong.
- Re-buzz / opponent chance: the opposing team can steal. During the steal window, an inline “Decide to steal in N…” countdown bar replaces the Correct / Wrong / No Steal (Time Up) buttons; when the countdown finishes, those buttons reappear so the host can resolve the steal.
- “Show Answer” toggle is available in the tally phases so the host can reveal or hide the intended answer.
- A Reset Game control clears scores and question usage and returns the board to a fresh state for another round.
-
Timer controls
- A small Timer Settings button in the top-right opens a popover that lets the host configure:
- Clue read delay (seconds before buzzers open).
- Buzzer lead time (how early to activate buzzers before the prep bar visually finishes).
- Answer timer length (for each team’s answer).
- Steal decision timer length (RE_BUZZ window).
- Whether to play a short sound when a timer hits zero.
- A small Timer Settings button in the top-right opens a popover that lets the host configure:
- Players join by navigating to the buzzer URL with a
?mode=buzzer&team=team1|team2query parameter. - Each phone shows:
- A large team-colored BUZZ button when buzzers are active.
- Clear status text, e.g.:
Host is reading the clue...BUZZ/STEALLocked in!/Wrong answer...Waiting for the next clue...
- Buzzer logic is enforced on the backend:
- Only one team can lock in at a time.
- During a re-buzz window, the first team cannot buzz again; only the other team can steal.
- Buzz attempts outside an active window are safely ignored.
-
Frontend
- Vite + Vue 3 + TypeScript.
- Tailwind CSS for styling.
- Composition API (
<script setup lang="ts">) used across components. - Two entrypoints in a single app:
- Host view (
App.vue) – scoreboard + board + question modal. - Buzzer view (
TeamBuzzer.vue) – phone-friendly buzzer client.
- Host view (
-
Backend
- AWS Amplify project with:
- AppSync GraphQL API.
- Lambda resolver (
GameController) implementing all game logic. - DynamoDB tables for
QuestionandGameState.
- AWS Amplify project with:
- Central
GameStaterecord in DynamoDB, keyed by a fixed ID (e.g.CURRENT_GAME). - Key fields (simplified):
status:SELECTING_CLUE | CLUE_ACTIVE | BUZZER_ACTIVE | BUZZER_LOCKED | RE_BUZZ.currentQuestionId,currentCategory,currentValue,currentClueText.buzzedTeam: which backend team is currently locked in.hasFirstAttemptedTeam: which team has already attempted this clue.autumnOrangeScore,seaFoamGreenScore.readDelayMs,clueActivatedAt,reBuzzWindowMs,reBuzzActivatedAt.
-
Queries
getBoard: returns all questions (category, value, clue, answer, used).getGameState: returns the currentGameState.
-
Mutations (all routed to
GameControllerLambda)selectClue(id: ID!, questionId: ID!): move toCLUE_ACTIVEand set the active question.activateBuzzers(id: ID!): transition fromCLUE_ACTIVEtoBUZZER_ACTIVE.buzz(id: ID!, teamId: TeamId!): lock in a team when buzzers are active.submitAnswerResult(id: ID!, isCorrect: Boolean!): score the current answer and either end the clue or open a re-buzz window.resetBuzzers(id: ID!): return toBUZZER_ACTIVEfromBUZZER_LOCKEDif needed.abandonQuestion(id: ID!): exit a clue without marking it used, resetting toSELECTING_CLUE.noSteal(id: ID!): end a clue inRE_BUZZwhen no steal happens, marking the question as used.resetGame(id: ID!): reset scores, game state, and questionusedflags.
-
Subscription
onGameStateChanged(id: ID!): pushes fullGameStatesnapshots to host and buzzer clients whenever any of the above mutations run.
-
useGameApi.ts- Wraps Amplify
API.graphqlcalls for all GraphQL queries, mutations, and subscriptions.
- Wraps Amplify
-
useGameState.ts- Holds a reactive
gameStateref shared across host and buzzer views. - Subscribes to
onGameStateChangedto keep clients in sync in real-time. - Exposes high-level methods:
selectClue,activateBuzzers,buzzForTeam,submitAnswer,abandonQuestion,noSteal,resetGame.
- Holds a reactive
-
useQuestions.ts+QuestionsRepository- Loads the full board from
getBoardand validates structure. - Exposes reactive
questionsandcategoriesto the host view. - Each question includes a
usedflag mirrored from DynamoDB so the board can grey out used cells even after reloads.
- Loads the full board from
- Host
App.vuemaintains local UI state:answeredCells: which board cells should be disabled.currentCategory,currentValue,buzzedTeam,opponentTeam,modalPhase,showAnswer.
- On first load, a small sync seeds
answeredCellsfrom backendusedflags. - A watcher on
gameStatereconstructs the question modal after a reload based on the backend status and fields, so the host can safely refresh the page mid-question without desynchronizing the game.
See AWS_DEPLOYMENT.md for step-by-step instructions to deploy the backend (Amplify) and frontend (Amplify Hosting or S3 + CloudFront) in your own AWS account.
This repo contains both the Amplify backend and the Vue frontend.
From frontend/:
npm install
npm run devOpen the printed local URL (default http://localhost:5173/).
- Host view (default):
http://localhost:5173/ - Buzzer view:
http://localhost:5173/?mode=buzzer&team=team1orteam2
The backend is managed by the Amplify CLI.
- Initialize / pull environment as needed:
amplify pull- After making schema or function changes:
amplify pushSee the Amplify docs for environment setup (AWS credentials, region, etc.).