Basket Parkour 3D
Making of a 3D browser demo: third-person parkour, shooting hoops, a level editor and a Claude-driven level generator.
Basket Parkour 3D is a tech demo: a third-person game where you run and jump across rooftops and building sites to score in every hoop before time runs out. It started with a goal that was more technical than playful: finding out how far a 3D game can go today in the browser, with nothing to install, built in small, verifiable steps together with Claude.
The stack
The game is written in TypeScript with Vite. Rendering is Three.js, with no UI framework: HUD, menus and results are plain HTML on top of the canvas. Physics is Rapier: the player is a kinematic character controller (a 1.8 m capsule), while the ball, barrels and crates are dynamic rigid bodies. Audio uses Howler, and behind it all sits a small PHP and MySQL backend for levels, progress and leaderboards.
One level format, three authors
Every level is a JSON file, and a single validator (LevelSchema.ts) decides what is valid. The same code is used by three different “authors”:
- the game, which turns the JSON into Three.js meshes and Rapier colliders;
- the browser editor, which runs the same engine as the game: object palette, gizmo, properties, 3D model import and a “Playtest” button to try the level right away;
- an MCP server running on my PC: I tell Claude “make me a rooftop level with 3 hoops and 2 defenders” and it builds the JSON, checks that every hoop can be reached from the platform path, calibrates the star thresholds and returns a link to play it.
No made-up scores
At the end of a level the result is not taken at face value: the server validates it again from scratch (minimum plausible time, the level’s theoretical maximum score, a hash binding time, score and player) before writing it to the leaderboard. Valid runs also go into the shared portale3d games leaderboard; being a demo, they don’t award Portal Points.
Performance: from 385 to 91 draw calls
To measure the engine there is a stress level generated by a script with a fixed seed: 300 blocks, 10 hoops, 8 defenders and 20 barrels. With one Three.js object per block it took 385 draw calls per frame; grouping the blocks into one InstancedMesh per material brings it down to 91 (−76%), while every block keeps its own physics collider.
Why it stays a demo
The game loop works: level menu, stars, defenders that steal the ball and go down to ball hits or shoulder charges, destructible barrels, an animated character. But the point of the demo was the architecture, not a finished game, and the question it left became the next step: a test of the same gameplay in Unreal Engine 5, to find out whether after two minutes of play it is more fun than the browser version.