06ONE TINY VILLAGE. ONE VERY BAD IDEA./Web Game
SNOWBALL
A low-poly 3D winter game about rolling one tiny snowball through a village and growing into an absolute unit.
- Role
- Creator & Developer
- Stack
- Three.js / JavaScript / Vite / Blender
- Year
- 2026
- Status
- Shipped
- Links
- Live app ↗
01Context
A self-contained 3D arcade game with a deliberately simple hook: turn one small snowball into the largest thing in a winter village.
02Problem
Make a quick-to-understand rolling game feel expressive and replayable while coordinating movement, collection, collisions, camera response, and a changing 3D world across desktop and touch devices.
03Constraints
- 01Keep the game client-only, with no API, database, or server-side game state.
- 02Make keyboard, gamepad, mouse, and touch controls work across desktop and narrow mobile layouts.
- 03Hold responsive frame times across a range of GPUs while rendering snow, weather, shadows, post-processing, and a populated village.
- 04Keep run progress, daily-goal progress, graphics settings, and accessibility preferences useful without account creation.
04Architecture
Vite builds a static browser game whose Three.js renderer and modular GameEngine coordinate player motion, physics, environment, scoring, audio, and DOM-based overlays. Small persistence helpers store personal settings and run records in localStorage.
- GameEngine and player
- Coordinate the update loop, run states, snowball deformation, camera modes, and tracking for collected props.
- Terrain and collision
- Share rolling terrain and collision data across snowball motion, obstacles, props, and camera placement; resolve collection, impacts, and wall slides.
- World and atmosphere
- Build the alpine village, NPC actors, snow and wind particles, and stage-based changes to weather, fog, lighting, and sound.
- Scoring and controls
- Combine combos, near misses, frenzy, momentum boost, daily goals, and stage progression with keyboard, touch, and gamepad input.
- Settings and audio
- Provide adaptive quality presets, dynamic resolution options, a post-processing toggle, synthesized Web Audio effects, and procedural music.
- Local game state
- Persist best score, best time, graphics preferences, and reduce-motion settings in the browser.
05Decisions
D1Keep the game as a static, client-side application
Why
The run, score, and personal settings do not require a server round trip or account service.
Result
The game can be hosted from static assets while keeping its state and interaction in the browser.
D2Split world, physics, player, scoring, audio, and UI into modules
Why
A single loop coordinates these systems, but separate modules make each part easier to tune as the game grows.
Result
Game behavior, scene construction, persistence, and overlays remain distinct parts of the Vite application.
D3Pair procedural models with a small set of sculpted terrain assets
Why
Code-built village props keep the world flexible, while authored mountain silhouettes add a stronger horizon.
Result
The game mixes lightweight generated scene objects with custom Blender mountain meshes and a fallback terrain form.
06Trade-offs
Layered atmosphere and post-processing around a low-poly world
Gained
- +A clear visual identity that reads from the overview camera
- +Weather and lighting can change over the course of a run
Gave up
- −Particles, shadows, and bloom need scalable quality settings for slower hardware
Store progress and preferences locally in the browser
Gained
- +No account step or backend dependency
- +Settings and best runs are available immediately on the same device
Gave up
- −Scores and preferences do not sync across devices
07Performance
- ↯Offer low-to-ultra graphics presets and individual controls for shadows, resolution, particles, draw distance, and post-processing.
- ↯Use dynamic resolution scaling as an option when maintaining frame time matters more than native render resolution.
- ↯Split the Three.js dependency into a dedicated vendor chunk and leave the game's own bundle separate.
08Challenges
- #Keep custom snowball dynamics, terrain height, collected props, and collision responses aligned as the ball changes size and speed.
- #Balance camera framing and control responsiveness between a wide desktop scene and thumb-operated mobile layouts.
- #Keep increasingly intense snow, wind, fog, lighting, and audio in step with stage progression while allowing graphics to scale down.
09Learnings
- →A compact game loop can feel rich when collection, near misses, momentum, collisions, and character reactions give each movement a readable response.
- →Controls and rendering quality need device-aware defaults when the same 3D scene serves keyboard, gamepad, and touch players.
- →Separating physics, world, audio, scoring, settings, and UI makes a growing client-side game easier to tune without adding a backend.
10Retrospective
A future pass could expand device-specific performance profiling and save optional run replays alongside the current local bests and daily goals.
11Gallery
The premise. Start as a small snowball at the edge of an alpine village. Roll downhill, collect anything smaller, and turn a quiet winter scene into a playful chain reaction.

The run. Skim larger obstacles to build momentum, hold boost to surge ahead, and smash through fences and cottages. A frenzy magnet pulls loose props toward you, while torch carriers can knock your haul away, so each run balances speed and risk.

The world. Snowfall, wind, fog, lighting, and music change with the stage. Villagers react before fleeing, daily goals give each session a small challenge, and the run builds toward becoming the ABSOLUTE UNIT.

The build. A modular Three.js and Vite client game combines a custom movement and collision loop, procedural village props, Blender-sculpted mountain ranges, generated Web Audio, responsive controls, and adjustable graphics presets.


Mobile · 390×844

Next project · 01
HELLO WORLD UNIVERSE →
A visual and technical anthology of computational history across programming languages.