Aakib Ansari.
Back to articles
Hands-on TestTested live

Gemini 3.1 Coding Test: I Built a 3D WebGL Racing Game in Three.js — AI Added Audio Synth & Minimap Nobody Asked For

Md Aakib Ansari
Md Aakib AnsariWeb Developer & AI Tools Reviewer
Updated 8 min readModel: Gemini 3.1 Pro
Gemini 3.1 Coding Test: I Built a 3D WebGL Racing Game in Three.js — AI Added Audio Synth & Minimap Nobody Asked For

I put Gemini 3.1 Pro to the test as a free coding ai tool to see if it qualifies as the best coding ai for browser game dev: one prompt to build a complete 3D arcade racing game in a single HTML file using Three.js, no frameworks, no external assets. The base build came back working on the first try. Getting it fully polished took five more rounds of bug fixes, one of which was a regression the model introduced while fixing something else.

The AI coding tool prompt: What I asked for

Building a complete webgl game in one file is a stringent test of spatial reasoning and physics architecture. The goal: a self-contained Three.js car game that opens by double-clicking index.html, no build steps, no external dependencies — a direct llm coding benchmark for agentic code generation.

Raw Prompt (Unedited)
Skip to clean version ↓

You are an expert Three.js game developer.

Build a complete 3D arcade racing game in ONE SINGLE HTML file using Three.js (load from CDN, no other libraries, no build tools, no external assets).

IMPORTANT:

  • Output ONLY one file: index.html
  • Put all HTML, CSS, and JavaScript inside this single file
  • Do not create any other files
  • Do not use React, TypeScript, or any frameworks
  • The final file must run by double-clicking index.html

TRACK:

  • A closed-loop race track built from simple geometry (a ribbon/road mesh or connected segments), with visible track boundaries so the player can tell where the drivable surface ends
  • At least a few curves and a couple of elevation changes or chicanes so it isn't just an oval
  • Simple scenery around the track (trees, barriers, or similar low-poly objects) so the world doesn't feel empty

PLAYER CAR:

  • Third-person chase camera that follows the car smoothly
  • Arcade-style driving physics: acceleration, braking, steering, and some drift/slide when turning at speed (does not need to be a full physics engine — a simplified arcade model is fine)
  • Controls: W/Up = accelerate, S/Down = brake/reverse, A/D or Left/Right = steer
  • Visual feedback for speed (camera FOV shift, motion blur, or particle trail are all acceptable — pick one)

RACE:

  • 3 AI-controlled opponent cars that follow the track using waypoints or a similar simple path-following method, at varying speeds
  • Lap counting: track when the player crosses the start/finish line, count laps, and finish the race after 3 laps
  • A basic position indicator (1st/2nd/3rd/4th) that updates as cars pass each other

UI:

  • HUD showing current lap (e.g. "Lap 2/3"), position, and speed
  • Race countdown at the start (3, 2, 1, GO)
  • End-of-race screen showing final position, with a restart button

CODE: everything inside <script> and <style> tags. No explanations. No markdown. No separate files. Return only the complete index.html code, loading Three.js from a CDN script tag.

Round one: base generation

The prompt came back with a track, car, AI opponents, and HUD all rendering immediately — but the countdown's "GO!" text stayed stuck on screen after the race started, sitting on top of the gameplay view instead of fading out.

Round two: camera and alignment

Fixing the GO-text overlay surfaced two more issues that needed their own follow-ups: the chase camera started pointed off to the side instead of behind the car, and separately, the car itself wasn't aligned with the road at spawn — it sat at an angle instead of facing straight down the first straight, visibly spinning during the fix before settling into place.

Round three: controls and a regression

Once the car was aligned, A/D and the arrow keys turned out to be swapped — pressing left turned the car right and vice versa. Fixing that broke something that had been working: the road mesh itself stopped rendering, a regression in a system unrelated to what was being changed. A follow-up fix restored it. One more pass was needed after that to fix the start/finish arch, which was misaligned with the road's actual heading and was effectively blocking the drivable surface instead of sitting cleanly over it.

By the end of six total rounds, the car spawned straight, controls matched the requested bindings, the road rendered, and the finish arch sat correctly over the track.

Total lines
1,421
File size
56 KB
Generation
One-shot base + 6 follow-up rounds
Framework
Three.js r128 (cdnjs)
Live Demo

Interactive Demo

W A S D  /  Arrow Keys to drive

It's a real, self-contained index.html — no build step, no server, opens by double-click exactly as instructed.

What the final build delivered: Gemini 3.1 Pro benchmark results

Track
17-point closed CatmullRom curve, real elevation change
Cars
Primitive-built (box/cylinder), 4 total (1 player + 3 AI)
Physics
Accel/brake/friction + lerped drift angle
AI
Curvature-lookahead speed control + lateral weave
Camera
Tangent-based chase cam, FOV widens with speed
Audio
Procedural Web Audio engine hum + tire screech (unprompted)
Minimap
Canvas 2D radar overlay (unprompted)
UI
Position, lap, lap time, speed, countdown, results screen

Player car and driving feel

The car model is built entirely from primitives — a wedge body, a dark cabin box, a glowing rim strip, a rear spoiler with struts, and four cylinder wheels with a colored inner rim — no external model or texture. Driving physics live in a Car class shared by player and AI: acceleration and braking are simple scalar ramps, and drift is handled by lerping a driftAngle toward a steering-derived target whenever you're steering hard above roughly 55% of top speed while braking or holding space, with a small speed penalty while drifting.

AI opponents

The three AI cars use a lightweight lookahead: each one samples the track tangent slightly ahead of its current position, compares it to its current heading, and slows proportionally to how sharp the upcoming curve is. Each AI also has its own aiSpeedFactor (0.9–1.05x) baked in at spawn, so they don't all lap at identical pace, and they occasionally re-target a random lateral offset to weave rather than driving a single fixed line.

Track and environment

The track itself isn't a flat ribbon — the 17 CatmullRomCurve3 control points vary in Y from -8 to 32, so there's real climbing and descending built into the loop, not just a top-down curve. Track and wall geometry are hand-built BufferGeometry meshes generated by walking the curve in 800 segments and extruding left/right edges, rather than using a primitive tube or plane. Scenery is 160 wireframe cone "trees" in alternating cyan/magenta scattered along both sides of the track at randomized offsets and heights.

Unprompted additions

Two things showed up that weren't anywhere in the prompt. The first is a full SoundManager class wired to the Web Audio API — not a placeholder beep, but an actual signal chain: a sawtooth oscillator through a lowpass filter for engine hum (pitch and gain scale continuously with speed), plus a separate looping noise buffer through a bandpass filter for tire screech that fades in once drift intensity crosses a threshold. The second is a canvas-based radar minimap in the bottom-left HUD, tracing the track outline and plotting all four cars as dots in real time — genuinely useful in play given how much the track curves, not just decorative. Our own game-build template specifically notes that minimaps haven't shown up unprompted in prior tests (GLM-5.2, Gemini 3.6 Flash) — this is the first time we've seen a model add one on its own.

UI and HUD

The HUD shows position (with ordinal suffix), current lap out of 3, a running lap timer, and a speedometer in km/h, all in a glassmorphism panel style with neon cyan/magenta accents. The countdown does a scaled/faded "3, 2, 1, GO!" sequence with a beep on each tick, and the results screen shows final position, total time, and best lap with a restart button that resets all four cars' track position, lap count, and speed. The track-following math holds up well throughout — position, orientation, and camera all derive from the same tangent/normal frame at parameter t, which is why the eventual alignment fix (rebuilding the car's rotation from right/normal/back basis vectors) was a clean, isolated change rather than a patch bolted on top of broken math.

What's missing

  • The base one-shot output was not launch-ready. Between the GO-text bug, the sideways camera, the car spawning misaligned, and inverted steering, most of the six fix rounds were addressing problems present in the very first generation — this wasn't a polished first pass.
  • One fix broke a working system. Fixing the inverted controls caused the road mesh to stop rendering in the next round — a genuine regression, not a pre-existing bug, introduced while editing an unrelated part of the code.
  • The start/finish arch needed a second geometry pass. Even after the car-alignment fix used correct tangent-based orientation, the lap gate arch was still misaligned with the road heading and had to be corrected separately.
  • No sound toggle or volume control despite the added audio system — engine and screech sound start on first keypress via AudioContext and have no mute button.

Honest assessment

Try it yourself

Try It Yourself

You are an expert Three.js game developer.

Build a complete 3D arcade racing game in ONE SINGLE HTML file using Three.js (load from CDN, no other libraries, no build tools, no external assets).

IMPORTANT:

  • Output ONLY one file: index.html
  • Put all HTML, CSS, and JavaScript inside this single file
  • Do not create any other files
  • Do not use React, TypeScript, or any frameworks
  • The final file must run by double-clicking index.html

TRACK:

  • A closed-loop race track built from simple geometry (a ribbon/road mesh or connected segments), with visible track boundaries so the player can tell where the drivable surface ends
  • At least a few curves and a couple of elevation changes or chicanes so it isn't just an oval
  • Simple scenery around the track (trees, barriers, or similar low-poly objects) so the world doesn't feel empty

PLAYER CAR:

  • Third-person chase camera that follows the car smoothly
  • Arcade-style driving physics: acceleration, braking, steering, and some drift/slide when turning at speed (does not need to be a full physics engine — a simplified arcade model is fine)
  • Controls: W/Up = accelerate, S/Down = brake/reverse, A/D or Left/Right = steer
  • Visual feedback for speed (camera FOV shift, motion blur, or particle trail are all acceptable — pick one)

RACE:

  • 3 AI-controlled opponent cars that follow the track using waypoints or a similar simple path-following method, at varying speeds
  • Lap counting: track when the player crosses the start/finish line, count laps, and finish the race after 3 laps
  • A basic position indicator (1st/2nd/3rd/4th) that updates as cars pass each other

UI:

  • HUD showing current lap (e.g. "Lap 2/3"), position, and speed
  • Race countdown at the start (3, 2, 1, GO)
  • End-of-race screen showing final position, with a restart button

CODE: everything inside <script> and <style> tags. No explanations. No markdown. No separate files. Return only the complete index.html code, loading Three.js from a CDN script tag.

FAQ

Frequently Asked Questions

Is Gemini 3.1 a good AI coding tool for game dev?
In our Gemini 3.1 coding test, the model performed exceptionally well as an AI coding tool for WebGL game development. When evaluated as the best coding ai candidate, it generated a 1,421-line WebGL racing game in one shot, complete with drift physics, AI opponents, and an unprompted synthesizer.
How does Gemini 3.1 compare as an AI game generator?
Gemini 3.1 Pro produced a structurally complete Three.js car game on the first pass — track geometry, physics, AI path-following, and HUD all working. Where it fell short as an AI game generator is polish: spawn alignment, control bindings, and one regression needed iterative fix rounds before the game was launch-ready.
Can Gemini 3.1 Pro build a 3D racing game?
Yes — Gemini 3.1 Pro on high effort generated a 1,421-line 3D arcade WebGL racing game in Three.js in one shot, featuring procedural track elevation, AI opponents, and drift physics. It required five follow-up fix rounds to resolve camera alignment and control regressions before it was fully polished.
Does Gemini 3.1 Pro add unprompted features?
Yes. In this test, Gemini 3.1 Pro added two features that were nowhere in the prompt: a procedural Web Audio sound synthesizer for engine pitch and tire screeches, and a 2D canvas radar minimap tracking all four cars in real time.
Can I run this locally?
Yes — it's a single index.html file with Three.js loaded from the cdnjs CDN. No build tools or server needed; just double-click it.
Was this really a 'one-shot' result?
The base game rendered and ran on the first prompt, but it needed six follow-up fix rounds before it was solid. We're calling that out plainly rather than labeling it a clean one-shot.
What's missing from the final build?
No mute/volume control for the added audio system, and no minimap zoom or track-position markers beyond car dots. Both are minor relative to the core race loop, which is fully functional.

Related Articles

Same Prompt, Two Models: Gemini 3.1 Pro vs GLM-5.2 Both One-Shot a Tower Defense Game — With Opposite Architectures
Hands-on Test9 min read
Same Prompt, Two Models: Gemini 3.1 Pro vs GLM-5.2 Both One-Shot a Tower Defense Game — With Opposite Architectures

We gave Gemini 3.1 Pro and GLM-5.2 the exact same tower defense prompt, both at max/high effort. Both one-shot a fully playable game with zero follow-up fixes — but they made opposite architectural choices on the one open question the prompt left them, and GLM-5.2 quietly added a fifth enemy type and a damage-type counter system nobody asked for.