You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
16 KiB
16 KiB
Kuldvillak/Jeopardy Game Platform - Technical Documentation
Table of Contents
- Overview
- Tech Stack
- Architecture
- Directory Structure
- Game Flow
- State Management
- Components
- Data Types
- Features
- Multi-Window Sync
Overview
Ultimate Gaming is a web-based platform for hosting trivia game shows, currently featuring Kuldvillak - a Jeopardy-style quiz game. The platform supports:
- Full game editor with customizable settings
- Dual-screen setup (Moderator + Projector views)
- Real-time cross-tab synchronization
- Theme customization
- Internationalization (Estonian/English)
- Audio management
- Persistent game storage
Tech Stack
| Technology | Version | Purpose |
|---|---|---|
| SvelteKit | 2.48.5 | Full-stack framework |
| Svelte 5 | 5.43.8 | UI with Runes reactivity |
| TailwindCSS | 4.1.17 | Utility-first styling |
| TypeScript | 5.9.3 | Type safety |
| Paraglide | 2.5.0 | i18n (internationalization) |
| Vite | 7.2.2 | Build tool |
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Ultimate Gaming │
├─────────────────────────────────────────────────────────────┤
│ Routes │
│ ├── / (Home - Game Selection) │
│ ├── /kuldvillak (Game Menu) │
│ ├── /kuldvillak/edit (Game Editor) │
│ └── /kuldvillak/play (Game Play) │
│ ├── ModeratorView (Controls) │
│ └── ProjectorView (Display) │
├─────────────────────────────────────────────────────────────┤
│ State Management (Svelte 5 Runes) │
│ ├── gameSession.svelte.ts (Live game state + BroadcastAPI) │
│ ├── kuldvillak.svelte.ts (Game store - editor) │
│ ├── theme.svelte.ts (Color theming) │
│ ├── audio.svelte.ts (Music/SFX volumes) │
│ └── persistence.ts (localStorage utilities) │
├─────────────────────────────────────────────────────────────┤
│ Components │
│ ├── Shared (Settings, ColorPicker, Toast, etc.) │
│ └── Kuldvillak UI (Buttons, Cards, Logo, etc.) │
└─────────────────────────────────────────────────────────────┘
Directory Structure
src/
├── lib/
│ ├── assets/ # Static assets (favicon)
│ ├── components/
│ │ ├── kuldvillak/
│ │ │ └── ui/ # Game-specific UI components
│ │ │ ├── KvButtonPrimary.svelte
│ │ │ ├── KvButtonSecondary.svelte
│ │ │ ├── KvEditCard.svelte
│ │ │ ├── KvGameLogo.svelte
│ │ │ ├── KvNumberInput.svelte
│ │ │ ├── KvPlayerCard.svelte
│ │ │ └── KvProjectorCard.svelte
│ │ ├── ColorPicker.svelte
│ │ ├── ConfirmDialog.svelte
│ │ ├── LanguageSwitcher.svelte
│ │ ├── Settings.svelte
│ │ ├── Slider.svelte
│ │ └── Toast.svelte
│ ├── paraglide/ # Generated i18n code
│ ├── stores/
│ │ ├── audio.svelte.ts
│ │ ├── gameSession.svelte.ts
│ │ ├── kuldvillak.svelte.ts
│ │ ├── persistence.ts
│ │ └── theme.svelte.ts
│ ├── types/
│ │ └── kuldvillak.ts # TypeScript interfaces
│ └── index.ts # Library exports
├── routes/
│ ├── kuldvillak/
│ │ ├── edit/
│ │ │ └── +page.svelte # Game editor
│ │ ├── play/
│ │ │ ├── +page.svelte # Route handler
│ │ │ ├── ModeratorView.svelte
│ │ │ └── ProjectorView.svelte
│ │ ├── +layout.svelte
│ │ └── +page.svelte # Game menu
│ ├── +error.svelte
│ ├── +layout.svelte # Root layout
│ ├── +page.svelte # Home page
│ └── layout.css # Global styles + Tailwind
├── app.html
├── app.d.ts
├── hooks.server.ts
└── hooks.ts
Game Flow
1. Game Creation (Editor)
/kuldvillak/edit
│
├── Configure Settings
│ ├── Number of rounds (1-2)
│ ├── Timer durations
│ ├── Point values (preset or custom)
│ ├── Daily doubles per round
│ └── Enable/disable final round
│
├── Add Teams (2-6 players)
│
├── Fill Categories & Questions
│ ├── 6 categories per round
│ ├── 5 questions per category
│ └── Mark daily doubles
│
├── Final Round Question (optional)
│
└── Start Game → Opens Moderator + Projector views
2. Game Phases
┌─────────────┐
│ intro │ ← Kuldvillak logo screen
└──────┬──────┘
│
┌──────▼──────────────┐
│ intro-categories │ ← Category reveal animation
└──────┬──────────────┘
│
┌──────▼──────┐
│ board │ ← Question selection grid
└──────┬──────┘
│
┌──────▼──────────────┐
│ daily-double (if) │ ← Wager selection
└──────┬──────────────┘
│
┌──────▼──────┐
│ question │ ← Display question, teams answer
└──────┬──────┘
│
├── Correct → Award points
├── Wrong → Deduct points, next team
└── Skip → Reveal answer
│
└──────► Back to board
│
│ (After all questions)
│
┌──────▼────────────────┐
│ final-intro │ ← Final round logo
├───────────────────────┤
│ final-category │ ← Reveal final category
├───────────────────────┤
│ final-question │ ← Teams write answers + wagers
├───────────────────────┤
│ final-scores │ ← Final standings
└───────────────────────┘
│
┌──────▼──────┐
│ finished │
└─────────────┘
3. Question Flow
Question Selected
│
├── Is Daily Double?
│ │
│ ├── YES → Select team → Enter wager → Show question
│ └── NO → Show question directly
│
▼
Display Question
│
├── Timer starts
│
├── Team clicks to answer
│ │
│ ├── Timer pauses
│ │
│ ├── Moderator marks Correct/Wrong
│ │ │
│ │ ├── Correct → +points → Show answer → Return to board
│ │ └── Wrong → -points → Continue (others can try)
│ │
│ └── All teams wrong → Auto-reveal answer
│
└── Timer expires → Auto-reveal answer
State Management
GameSessionStore (gameSession.svelte.ts)
The heart of the game - manages live game state with cross-tab synchronization.
Key Features:
- Uses
BroadcastChannelAPI for real-time sync between Moderator and Projector - Persists to
localStoragefor session recovery - Single internal timer managed by moderator view
State Interface:
interface GameSessionState {
// Game data
name: string;
settings: GameSettings;
teams: Team[];
rounds: Round[];
finalRound: FinalRound | null;
// Game phase
phase: GamePhase;
currentRoundIndex: number;
activeTeamId: string | null;
// Animation state
introCategoryIndex: number;
categoriesIntroduced: boolean;
boardRevealed: boolean;
// Question state
currentQuestion: { roundIndex, categoryIndex, questionIndex } | null;
showAnswer: boolean;
wrongTeamIds: string[];
lastAnsweredTeamId: string | null;
lastAnswerCorrect: boolean | null;
// Daily Double
dailyDoubleWager: number | null;
// Final Round
finalCategoryRevealed: boolean;
finalWagers: Record<string, number>;
finalAnswers: Record<string, string>;
finalRevealed: string[];
// Timer
timerRunning: boolean;
timerSeconds: number;
timerMax: number;
timeoutCountdown: number | null;
revealCountdown: number | null;
skippingQuestion: boolean;
// Tracking
questionsAnswered: number;
currentQuestionNumber: number;
questionResults: QuestionResult[];
}
ThemeStore (theme.svelte.ts)
Manages color theming with cross-window sync.
CSS Variables controlled:
--kv-blue(primary)--kv-yellow(secondary)--kv-text--kv-background
AudioStore (audio.svelte.ts)
Manages background music and sound effects volumes.
Components
Shared Components
| Component | Purpose |
|---|---|
Settings.svelte |
Modal with audio, language, and color settings |
ColorPicker.svelte |
Full HSL color picker with swatches |
ConfirmDialog.svelte |
Confirmation modal |
Toast.svelte |
Notification messages |
Slider.svelte |
Volume control slider |
LanguageSwitcher.svelte |
ET/EN language toggle |
Kuldvillak UI Components
| Component | Purpose |
|---|---|
KvButtonPrimary.svelte |
Yellow primary action button |
KvButtonSecondary.svelte |
Blue secondary action button |
KvEditCard.svelte |
Team card for moderator (score adjust, correct/wrong) |
KvPlayerCard.svelte |
Basic player display card |
KvProjectorCard.svelte |
Player card for projector view |
KvNumberInput.svelte |
Styled number input with +/- buttons |
KvGameLogo.svelte |
SVG logo component with variants |
Data Types
Core Types (types/kuldvillak.ts)
interface Question {
id: string;
question: string;
answer: string;
points: number;
isDailyDouble: boolean;
isRevealed: boolean;
imageUrl?: string;
}
interface Category {
id: string;
name: string;
questions: Question[];
}
interface Round {
id: string;
name: string;
categories: Category[];
pointMultiplier: number;
}
interface Team {
id: string;
name: string;
score: number;
}
interface GameSettings {
numberOfRounds: 1 | 2;
pointValuePreset: 'round1' | 'round2' | 'custom' | 'multiplier';
pointValues: number[];
basePointValue: number;
categoriesPerRound: number; // Default: 6
questionsPerCategory: number; // Default: 5
dailyDoublesPerRound: number[];
enableFinalRound: boolean;
enableSoundEffects: boolean;
allowNegativeScores: boolean;
maxTeams: number; // Max: 6
defaultTimerSeconds: number;
answerRevealSeconds: number;
}
type GamePhase =
| 'intro' // Logo screen
| 'intro-categories' // Category reveal
| 'lobby'
| 'board' // Question grid
| 'question'
| 'answer'
| 'daily-double'
| 'final-intro'
| 'final-category'
| 'final-wagers'
| 'final-question'
| 'final-reveal'
| 'final-scores'
| 'finished';
Features
1. Game Editor
- Auto-save to localStorage
- Import/Export JSON game files
- Visual daily double indicators
- Point value presets or custom
- Final round configuration
2. Moderator View
- Full game control
- Score adjustments
- Timer control (start/stop/reset)
- Question skip functionality
- Open projector window button
3. Projector View
- Full-screen presentation
- Animated category introductions
- Staggered board reveal animation
- Question expand animation
- Team score display during questions
- Final scores display
4. Animations (ProjectorView)
- Category Intro: 500ms fade-in → 1500ms shown → 500ms push-out
- Board Reveal: Staggered 50ms per cell
- Question Expand: From grid cell position to fullscreen
5. Cross-Tab Sync
- Real-time state sync via BroadcastChannel
- localStorage persistence for recovery
- Single timer owner (moderator)
Multi-Window Sync
┌─────────────────┐ BroadcastChannel ┌─────────────────┐
│ Moderator Tab │ ◄─────────────────────────────► │ Projector Tab │
│ │ │ │
│ - Controls │ STATE_UPDATE │ - Display │
│ - Timer owner │ ─────────────────────────────────►│ - Animations │
│ - Score adjust │ │ - Read-only │
└────────┬────────┘ └─────────────────┘
│
│ localStorage
▼
┌─────────────────┐
│ Session Store │
│ (persistence) │
└─────────────────┘
Styling
Design Tokens (CSS Variables)
:root {
/* Colors */
--kv-blue: #003B9B; /* Primary */
--kv-yellow: #FFAB00; /* Secondary/Accent */
--kv-text: #FFFFFF;
--kv-background: #000000;
--kv-green: #009900; /* Correct */
--kv-red: #FF3333; /* Wrong */
/* Fonts */
--kv-font-title: 'Swiss 921';
--kv-font-body: 'Swiss 921';
--kv-font-price: 'Bebas Neue';
--kv-font-question: 'ITC Korinna';
/* Shadows */
--kv-shadow-text: 6px 6px 4px rgba(0,0,0,0.5);
--kv-shadow-price: 8px 8px 4px rgba(0,0,0,0.5);
--kv-shadow-button: 0 4px 4px rgba(0,0,0,0.25), 8px 8px 4px rgba(0,0,0,0.5);
}
Tailwind Theme Extensions
Custom Tailwind classes prefixed with kv-:
- Colors:
bg-kv-blue,text-kv-yellow, etc. - Fonts:
font-kv-body,font-kv-price, etc.
i18n (Internationalization)
Using Paraglide for type-safe translations.
Supported Languages:
- Estonian (et) - Primary
- English (en)
Message Files:
messages/et.jsonmessages/en.json
Usage:
<script>
import * as m from "$lib/paraglide/messages";
</script>
{m.kv_play_daily_double()}
Development
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run build
# Type check
npm run check
Future Considerations
- Database Backend: Replace localStorage with proper database for persistence
- WebSocket: Real-time sync without BroadcastChannel limitations
- Sound Effects: Implement actual SFX playback
- More Game Modes: "Rooside Sõda" (Family Feud) placeholder exists
- User Accounts: Save games to cloud
- Mobile Buzzer: Player buzzer app for team selection