A 3D platformer where every level is a real web page. Fix the HTML, CSS or JavaScript, and walk across what you built.
- Overview
- Screenshots
- The course
- How it works
- Tech stack
- Getting started
- Controls
- Project structure
- Adding a lesson
- License
DOMAIN teaches web development from zero by turning code into terrain.
Each level is a genuine HTML document. The browser lays it out, the game measures every element, and each one becomes a block in a floating 3D world. Every level starts broken: a bridge too short, a gate that won't open, stairs built in the wrong order. You learn one idea, change the code, and the world rebuilds so you can cross it.
As you finish lessons you also build a real website, the Anthill Bakery. It starts as a blank page. HTML gives it content, CSS gives it style, Flexbox arranges it, and JavaScript makes it interactive, one piece per lesson.
Highlights
- 120 lessons across HTML, CSS, Flexbox and JavaScript, each with an explanation, annotated example, steps, a reference card, five progressive hints, a debrief and a quiz.
- The real browser is the rules engine. CSS is never simulated: if the browser renders it, the world matches it.
- JavaScript runs for real, with loop guards, beginner-friendly errors, a console, modules, a pretend server for
fetch, and sandboxed storage. - A website that grows with you, shown after every lesson as a before-and-after reveal.
- It feels like a game: dust and confetti, a level-clear celebration, XP, eight builder ranks and a daily streak — and the world tells you the moment your code makes the goal reachable.
- In-world DevTools: click any block to inspect its element, box, CSS rules and event listeners, or press F to see the flat page.
![]() The landing page proves the mechanic: change a property, watch the ground move. |
![]() The website you build, one piece per lesson. |
flowchart LR
A["<b>1 · HTML</b><br/>20 lessons<br/><i>what things are</i>"] --> B["<b>2 · CSS</b><br/>38 lessons<br/><i>how they look</i>"]
B --> C["<b>3 · Flexbox</b><br/>12 lessons<br/><i>how they're arranged</i>"]
C --> D["<b>4 · JavaScript</b><br/>50 lessons<br/><i>how they change</i>"]
D --> E(["A finished, interactive website"])
| Chapter | Lessons | Topics |
|---|---|---|
| HTML | 20 | Elements, attributes, nesting, div/span, semantic sections, id/class, data-*, headings, paragraphs, emphasis, lists, links, target/rel, images, forms, validation, tables, the <head>, alt text |
| CSS | 38 | Selectors and combinators, pseudo-classes and pseudo-elements, specificity, inheritance, !important, the box model, units, calc/clamp, colour, positioning, z-index, overflow, Grid, backgrounds, shadows, gradients, typography, custom properties, transitions |
| Flexbox | 12 | Container vs item, display: flex, direction, wrap, justify-content, align-items, align-content, gap, grow/shrink/basis, align-self/order, the min-width: auto gotcha, patterns and when to use Grid |
| JavaScript | 50 | let/const, types, references, equality and truthiness, template literals, operators, control flow, loops, functions, hoisting, scope, closures, this, callbacks, arrays, objects, JSON, the DOM, events, delegation, timers, promises, async/await, Promise.all, fetch, loading and error states, modules, storage, debugging, classes, Map/Set, regex, dates, strict mode |
flowchart TB
subgraph Editor["Code panel · CodeMirror 6"]
HTML[HTML] ~~~ CSS[CSS] ~~~ JS[JavaScript]
end
Editor -->|"validated edits"| Store[("Game state<br/>Zustand")]
Store --> Stage
subgraph Stage["Hidden stage · same-origin iframe"]
Layout["Browser layout engine"] --> Measure["getBoundingClientRect()<br/>getComputedStyle()"]
end
JS -->|"Run"| Runner["JavaScript runner"]
Runner -->|"changes the DOM"| Stage
Measure -->|"world objects"| World
subgraph World["3D world · React Three Fiber"]
Blocks["Blocks & containers"] --- Physics["Box-based platformer physics"]
Physics --- Camera["Player & camera"]
end
World -->|"reaches the goal"| Debrief["Debrief · rubric · quiz"]
Debrief --> Progress[("Saved progress<br/>localStorage")]
Progress --> Site["Anthill Bakery site<br/>one piece per lesson"]
- You edit code. HTML and CSS are checked as you type, and invalid code never reaches the world, so a half-typed line can't break it.
- The browser lays out the page inside a hidden iframe fixed at 900 × 520 px.
- Every element is measured and becomes a world object. Elements with no child elements are solid, walkable blocks; elements with children are drawn as translucent frames.
- The 3D scene and physics read those same objects, so what you see, what you stand on and what the code says always agree.
- Reaching the goal runs the lesson's rubric for a gold seal, saves your progress, and adds that lesson's piece to your website.
sequenceDiagram
actor Learner
participant Game
participant Stage as Hidden page
participant World as 3D world
Game->>Learner: Lesson intro and what it adds to your site
Game->>Stage: Load the broken HTML, CSS and JS
Stage->>World: Measured layout: the gap is too wide
Learner->>World: Tries to cross and falls
Learner->>Game: Reads the lesson, asks for hints, edits the code
Game->>Stage: Apply the fix
Stage->>World: New layout: the bridge reaches
Learner->>World: Walks to the goal
Game->>Learner: Debrief, gold seal, quiz and "your website grew"
Every level is designed around the character's measured reach, about 200 px across and 100 px up. A broken layout is always clearly out of reach and a fixed one clearly within it, so your code decides the outcome, not your timing.
flowchart LR
Code["Your code"] --> Parse["Parse and syntax check<br/>(acorn)"]
Parse --> Modules["import / export<br/>rewritten to module lookups"]
Modules --> Guards["Loop guards inserted"]
Guards --> Run["Runs in the level's page<br/>(strict mode)"]
Run -.-> Console["Console<br/>log · table · count · group · time"]
Run -.-> Fetch["fetch<br/>pretend server with status codes and delays"]
Run -.-> Storage["localStorage / sessionStorage<br/>sandboxed per level"]
Run -.-> Errors["Friendly error messages"]
- Loop guards stop runaway loops after 10,000 turns and explain why, instead of freezing the tab.
- Modules: lessons can ship their own module files, and
import/exportwork between them. fetchtalks to a small pretend server defined by the level. Requests really wait, return realResponseobjects, and can fail with400,404or503, which is enough to teach loading and error states properly.- Storage is isolated per level and never touches the game's own saved progress.
- Errors are translated into plain language, like “Tried to use
.forEachon nothing (null)”, with a hint about the likely cause.
| Layer | Technology | Used for |
|---|---|---|
| Framework | Next.js 16 (App Router, Turbopack) | Routing, the landing page and /play, production builds |
| UI | React 19 | Every screen, panel and overlay |
| Language | TypeScript 5 | The whole codebase, including the lesson data |
| 3D rendering | Three.js r186 · React Three Fiber 9 · drei 10 | The floating world: blocks, sky, mountain ridges, clouds and islands |
| Post-processing | @react-three/postprocessing 3 | Bloom, ambient occlusion (N8AO), anti-aliasing (SMAA) and vignette |
| Physics | Custom box-based platformer controller | Movement, jumping and collisions against the measured page boxes |
| Code editor | CodeMirror 6 via @uiw/react-codemirror |
HTML, CSS and JavaScript editing, syntax highlighting, inline errors |
| JavaScript parsing | acorn · acorn-walk | Syntax checking, loop guards, module rewriting |
| HTML & CSS checks | Custom parsers (htmlParse.ts, cssParse.ts) |
Beginner-friendly validation and the lesson rubrics |
| State | Zustand 5 (persist middleware) |
Game state and saved progress in localStorage |
| Styling | Tailwind CSS 4 | Interface styling, theme tokens, the landing page |
| Fonts | next/font with Google Fonts |
Cinzel, Barlow, Barlow Condensed, JetBrains Mono |
| Audio | Web Audio API | Procedural sound effects, with no audio files |
| Textures | Canvas 2D | Procedurally drawn surface textures, with no image files |
| Quality | ESLint 9 (eslint-config-next) |
Linting |
Browser APIs the game relies on: getBoundingClientRect, getComputedStyle, MutationObserver, ResizeObserver, structuredClone, and the Fetch Response object.
Play the deployed game at domain-indol-nine.vercel.app — nothing to install. To run it yourself:
Requirements: Node.js 20.9 or newer, and npm.
git clone https://github.com/shreyascode11/Domain.git
cd Domain
npm install
npm run devOpen http://localhost:3000 and choose Start dreaming, or go straight to /play.
| Command | What it does |
|---|---|
npm run dev |
Start the development server |
npm run build |
Create a production build |
npm run start |
Serve the production build |
npm run lint |
Run ESLint |
Tip for development: lessons unlock in order. To open them all while working on the game, set
UNLOCK_ALL_LEVELStotrueinlib/store.ts.
| Input | Action |
|---|---|
| A D or ← → | Move |
| Space | Jump |
| V | Switch between 3D and side view |
| F | Show the flat web page |
| R | Respawn at the start |
| M | World map |
| B | The website you're building |
| Ctrl/⌘ + Enter | Run JavaScript |
| Right-drag · scroll | Look around · zoom |
| Click a block | Inspect it like DevTools |
Domain/
├── app/
│ ├── layout.tsx Fonts and the root layout
│ ├── page.tsx Landing page
│ ├── play/page.tsx The game
│ └── globals.css Theme tokens and shared styles
├── components/
│ ├── PlayClient.tsx The game screen: world, panels, shortcuts
│ ├── HiddenStage.tsx The iframe page the world is measured from
│ ├── Scene3D.tsx Sky, ridges, clouds, islands and post-processing
│ ├── Platform.tsx Blocks and container frames
│ ├── Player.tsx Character, camera and controls
│ ├── CodePanel.tsx Learn / HTML / CSS / JS tabs and the console
│ ├── LessonPanel.tsx Lesson content and hints
│ ├── Inspector.tsx In-world DevTools
│ ├── Overlays.tsx Intro, debrief, quiz, map, chapter complete
│ ├── SiteBuild.tsx "Your website grew", My Site, landing showcase
│ ├── SitePreview.tsx The browser-window site preview
│ ├── TitleScreen.tsx Landing page (with DreamSky.tsx)
│ └── WorldMap.tsx The course map
├── lib/
│ ├── levels.ts Chapters, plus the HTML, CSS and Flexbox lessons
│ ├── jsLevels/ The 50 JavaScript lessons and their world kit
│ ├── jsRun.ts The JavaScript runner
│ ├── layout.ts · stage.ts Measured elements → world objects
│ ├── physics.ts Platformer controller and reach constants
│ ├── htmlParse.ts · cssParse.ts Validation and rubric helpers
│ ├── site.ts · siteJs.ts The Anthill Bakery site, one piece per lesson
│ ├── store.ts Game state and saved progress
│ └── audio.ts · textures.ts · biomes.ts Sound, textures, chapter palettes
└── hooks/ Keyboard input
A lesson is a single object in lib/levels.ts, or in lib/jsLevels/ for JavaScript.
flowchart LR
A["Write the lesson<br/>explanation, example, steps,<br/>reference, 5 hints, quiz"] --> B["Build the broken level<br/>html · css · js"]
B --> C["Check the geometry<br/>broken: out of reach<br/>fixed: within reach"]
C --> D["Write the rubric<br/>what earns gold"]
D --> E["Add its website piece<br/>site.ts / siteJs.ts"]
| Field | Purpose |
|---|---|
lesson, example, steps, reference |
What the learner reads |
hints |
Five rungs: notice, question, narrow, re-teach, reveal |
debrief, quiz |
The takeaway and a check for understanding |
html, css, js, edit |
The starting files, and which one the learner edits |
rubric |
Decides whether the solution earns the gold seal |
modules, api, storage |
Optional extras for JavaScript lessons |
© 2026 Shreyas. All rights reserved. See LICENSE.

