Skip to content

About

A tiny 3 × 3 world. Twelve handcrafted puzzles. A cozy browser game built with TypeScript and Three.js.

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

Little Loop

A very small world. A proper little puzzle game.

Guide a small, leaf-headed robot around a world of exactly nine tiles. Push the sun, moon, and star stones onto their matching sockets. Step or push off an edge and arrive on the opposite side of the same row or column.

The nine-tile world of Little Loop

Twelve authored puzzles introduce the rules gently, then grow into compact spatial challenges. The final puzzle has a shortest solution of 21 moves. There are no timers, lives, ads, accounts, or external runtime services.

  • Procedural 3D diorama, original robot and stone models, soft shadows, and animated edge transitions.
  • Unlimited undo, restart, shortest-path hints, deadlock detection, and perfect-solution scores.
  • Saved unlocks, personal bests, and sound preference in your browser.
  • Keyboard, mouse, and touch controls; responsive desktop and mobile layouts.
  • Symbols accompany colors, reduced motion is respected, dialogs support the keyboard, and board positions are announced for screen readers.
  • A fully playable top-down board automatically takes over if WebGL is unavailable.
  • Original synthesized sounds and bundled fonts. Everything is served from the same origin.

Play locally

Requires Node.js 24 or newer.

npm ci
npm run dev

Open the local URL printed by Vite, usually http://localhost:5173. The production build is npm run build; serve it locally with npm run preview.

Action Controls
Move / push Arrow keys, WASD, direction buttons, or tap a tile in the robot’s row or column
Undo Z or Backspace
Restart R
Hint H
Close a dialog Escape

The direction buttons follow the board’s isometric axes. Each stone has its own matching symbol. You can push one stone at a time; you cannot pull. Rocks never move. Moves, stones, and rows wrap on both axes.

Code structure

The game uses TypeScript, Three.js, and Vite, without a UI framework. Game state is immutable and independent of rendering.

src/
  game/
    rules.ts          Pure movement rules and breadth-first solver
    levels.ts         Puzzle definitions and tutorial copy
    progress.ts       Validated, versioned, failure-tolerant browser storage
    rules.test.ts     Rule invariants and shortest-path checks for every level
  scene.ts            Procedural models, rendering, picking, and animation
  audio.ts            Small Web Audio synthesizer, initialized on user interaction
  ui/icons.ts         Inline vector icons
  main.ts             Game controller, dialogs, input, and accessible 2D fallback
  style.css           Responsive interface and reduced-motion behavior
tests/game.spec.ts    Desktop/mobile playthroughs and accessibility checks

Cells are numbered row-first from 0 to 8. A level’s goals[i] is the target for initial.stones[i]: identities must be preserved. Add a Level to src/game/levels.ts and its expected shortest path length to the unit test to extend the journey. The interface builds its navigation and puzzle totals from that array.

The offline design tool enumerates solvable starts for an arrangement and reports the most distant candidates:

npm run levels:explore -- --walls=4 --goals=2,5,8

Verification

npm run check                  # Rules, all puzzle solutions, TypeScript, production build
npx playwright install chromium
npm run test:e2e               # Full desktop/mobile journeys, UI, storage, fallback, motion, accessibility

The end-to-end suite uses real Chromium with software WebGL and automatically starts its own dev server. To test a deployed build, set GAME_URL to its URL instead. Tests never expose gameplay or solver internals on window.

GitHub Actions runs the same checks before deploying dist/ to GitHub Pages. Pull requests run checks without publishing. Vite uses relative asset URLs, so the build also works under a repository subpath or another static host.

Credits and license

Game code, procedural art, icon drawings, and synthesized sounds: MIT. Three.js is MIT licensed. DM Sans and Fraunces are distributed under the SIL Open Font License; their full notices are included in public/licenses. See THIRD_PARTY_NOTICES.md.

About

A tiny 3 × 3 world. Twelve handcrafted puzzles. A cozy browser game built with TypeScript and Three.js.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages