A faithful browser-based recreation of the NES classic Duck Hunt, built with TypeScript, Go, and the Web Audio API. No external audio files or game ROMs — every sound is synthesized from scratch and every sprite is rendered from an original PNG sprite sheet.
- Authentic NES gameplay — flying ducks, dog intro animation, per-round scoring, and lives system
- 3 duck color variants with sprite-sheet animations (right-flight, up-flight, hit, falling)
- Dog animation sequence — runs in, sniffs toward center, perks up on alert, jumps into bushes, then emerges with duck(s) or mocks the player
- NES-accurate sprite background — sky, trees, and bush foreground with correct depth layering
- Synthesized audio — gunshot, duck hit, duck escape, dog laugh, score fanfare, and looping background music (all via Web Audio API, zero audio files)
- Scoring system — 500 pts per duck + 500 flat perfect-round bonus + consecutive-perfect streak multiplier (×10% per streak)
- Arcade leaderboard — top-10 scores with 3-letter initials entry, persisted server-side (or falls back to
localStoragewithout the Go server) - Pause / quit — ESC to pause, Q to quit to game over, M to mute
- Fully containerized — single
docker compose up --buildto run
| Layer | Technology |
|---|---|
| Frontend | TypeScript · Vite · Canvas 2D · Web Audio API |
| Backend | Go 1.22 · net/http (no framework) |
| Container | Docker multi-stage build · Docker Compose |
| Audio | Web Audio API (oscillators, noise buffers, envelope shaping) |
| Graphics | HTML5 Canvas + PNG sprite sheet |
Requires: Docker and Docker Compose.
git clone https://github.com/jwhitten37-dev/duck-hunt-web.git
cd duck-hunt
docker compose up --buildOpen http://localhost:8080 and click to start.
Sprite sheet: place your
sprites.pngatfrontend/public/sprites.pngbefore building. The game falls back to procedural canvas graphics without it.
Three workflows are available depending on what you're working on:
make serve
# http://localhost:8080Builds the frontend once with Vite, then serves everything from Go. Re-run after frontend changes.
# Terminal 1 — Go API
make dev-server
# Terminal 2 — Vite dev server with HMR
make dev-frontend
# http://localhost:5173Vite proxies /api calls to the Go server automatically.
docker compose up --build
# http://localhost:8080Leaderboard scores persist in a named Docker volume (scores-data). Use docker compose down -v to wipe the board.
duck-hunt/
├── cmd/server/ # Go HTTP server — static file serving + scores API
├── frontend/
│ ├── public/ # Static assets (sprites.png goes here)
│ └── src/game/
│ ├── Animator.ts # Frame-cycling utility
│ ├── Dog.ts # Dog intro + scene actor
│ ├── Duck.ts # Duck entity (flight, hit, fall)
│ ├── Game.ts # Root game loop + state machine
│ ├── HUD.ts # Score/ammo/lives display
│ ├── InputManager.ts # Mouse/click input
│ ├── LeaderboardScreen.ts # Arcade-style top-10 display
│ ├── NameEntryScreen.ts # 3-letter initials entry
│ ├── Scene.ts # Background + foreground rendering
│ ├── ScorePopup.ts # Floating "+N" score popups
│ ├── SoundManager.ts # All audio synthesis
│ ├── SpriteMap.ts # Sprite sheet frame coordinate table
│ ├── SpriteSheet.ts # Sprite atlas loader + draw utilities
│ └── TitleScreen.ts # Animated title card
├── Dockerfile
├── docker-compose.yml
├── Makefile
└── go.mod
| Input | Action |
|---|---|
| Click | Shoot / Start game / Resume |
| ESC | Pause |
| Q (while paused) | Quit to game over |
| M | Toggle mute |
| Arrow keys | Navigate initials entry |
| Enter / Space | Confirm initial |
| Event | Points |
|---|---|
| Duck hit | +500 |
| Perfect round (all ducks hit) | +500 bonus |
| Consecutive perfect streak | +(round total × streak × 10%) |
| Duck escaped | 0 |
| Round with zero hits | −1 life |
The Go server exposes a minimal scores API:
GET /api/scores → returns top-10 as JSON array
POST /api/scores → { "name": "AAA", "score": 1500 } → returns updated top-10
GET /healthz → "ok" (used by Docker healthcheck)
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP listen port |
STATIC_DIR |
./static |
Path to the compiled frontend assets |
SCORES_FILE |
./scores.json |
Path to the leaderboard JSON file |
- Sprite sheet — sourced from The Spriters Resource (Duck Hunt — NES). Credit to the original ripper(s) listed on that page.
- Original game — Duck Hunt © Nintendo, 1984. All character designs, gameplay concepts, and audio motifs belong to Nintendo.
This project is an unofficial fan recreation made for educational and personal use only. It is not affiliated with, endorsed by, or sponsored by Nintendo Co., Ltd. Duck Hunt and all associated characters are trademarks of Nintendo.
No Nintendo ROMs, audio files, or proprietary assets are distributed with this project. The sprite sheet (sprites.png) is not included in this repository — you must supply your own copy before building. The synthesized audio is an original implementation using the Web Audio API.
This project is not intended for commercial use.
MIT — see LICENSE.