Guides
Browser games built with pure CSS or vanilla JavaScript, and why the distinction matters
A pure CSS game has no JavaScript at all — state lives in checkboxes and the whole thing is a constraint puzzle. A vanilla JS game has no framework. Both are worth playing for different reasons.
- Topic
- Open source
- Difficulty
- beginner
- Spoilers
- None
“Built with pure CSS or vanilla JS” gets used as a single compliment, as if they were the same achievement. They are not. One is a party trick with real depth. The other is a statement about dependencies. Both produce games worth playing, and the difference tells you something about the state of web development.
Pure CSS games: no JavaScript whatsoever
A pure CSS game runs with scripting disabled. Not “barely any JavaScript” — none. If you turn JavaScript off in your browser, it still works.
That constraint sounds impossible until you remember that CSS can hold state in exactly two places: form controls and :target. Everything a pure CSS game does is built on those.
The classic example is tic-tac-toe, and there are several real implementations to learn from:
- AJ Spetner’s no-JS tic-tac-toe — written up in detail on dev.to with the source on CodePen. No preprocessors, no build step.
ricardodorosario/tic-tac-toe-pure-css— the same idea, structured as a repository.lukebatchelor/css-tic-tac-toe— an implementation that supports both a two-player mode and a computer opponent.
The technique is worth understanding because it explains the whole genre. Nine radio buttons hold the board state. A click marks a square with a pseudo-element. Win detection is the interesting part: you cannot loop, so you enumerate. Either you write out every winning line as a selector, or you use CSS counters and :has() to count marks in a row. Modern :has() widened what is possible here considerably, which is why pure CSS games got better in the last few years.
What pure CSS games are genuinely good at: puzzles where the state space is small and the moves are discrete. Tic-tac-toe, Sokoban variants, memory games, quizzes, anything turn-based on a small board.
What they cannot do: real-time anything. Randomness. Sound. A timer that matters. There is no clock and no Math.random(), so any game needing those is out of the genre by definition.
If you want to learn CSS properly, writing one of these will teach you more than a year of styling landing pages. Selector specificity, sibling combinators, :checked, :has(), counter() and the cascade as an actual computation engine — all of it becomes load-bearing.
Vanilla JavaScript games: no framework, no build step
The other category is different. These have JavaScript, sometimes a lot of it, but no React, no Vue, no bundler, no node_modules. You can open the source in a text editor and read the whole game.
That is a real property, not a nostalgic one. A vanilla JS game has no build step, so what you read is what runs. There is no transpiled output to reverse-engineer, no framework internals to hold in your head, and no dependency tree that breaks when someone unpublishes a package.
We host several on this site, and they are all plain JavaScript:
- 2048 — the sliding-tile puzzle by Gabriele Cirulli. Its entire state is a 4×4 grid of numbers, and the movement logic is a handful of functions. It is one of the best pieces of readable game code ever written.
- Hextris — a Tetris-adjacent rotation game with a genuinely subtle combo timer.
- Asteroids — Doug McInnes’s canvas reimplementation of the 1979 arcade game, with the inertia physics intact and a set of developer debug keys left in.
- Tiny Platformer — a fixed-timestep platformer driven by a single JSON level file.
All four are MIT or GPL licensed and all four are self-hosted here, which means you can read the source of the version you are playing.
The comparison that actually matters
| Pure CSS | Vanilla JS | |
|---|---|---|
| Scripting required | None at all | Yes |
| State lives in | Form controls and :target |
Variables |
| Real-time possible | No | Yes |
| Randomness possible | No | Yes |
| Build step | None | None |
| Teaches you | The cascade, as a system | Game loops and state |
The shared property is the important one: neither has a build step. In both cases the thing you read is the thing that runs. That is increasingly rare, and it is the reason games in these two categories are so much better to learn from than a bundled framework build.
Start here
If you want to write one, write tic-tac-toe in pure CSS first. It is a small, bounded problem with a known solution, and finishing it teaches you something no tutorial will. Then read the source of 2048 and notice how little code it takes to make something people played for years.