---
title: "Snake — ADI Store"
description: "A grid snake with buffered turning, wall/wrap modes, a bonus pickup that times out, and speed that ramps with length."
url: https://withadi.dev/store/game-snake/
---

[All apps](https://withadi.dev/store/)

![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><rect width='48' height='48' rx='10' fill='%23161616'/><rect x='10' y='14' width='9' height='9' rx='2' fill='%234CB77A'/><rect x='19' y='14' width='9' height='9' rx='2' fill='%234CB77A'/><rect x='28' y='14' width='9' height='9' rx='2' fill='%235FD191'/><rect x='10' y='23' width='9' height='9' rx='2' fill='%234CB77A'/><circle cx='33' cy='33' r='4' fill='%23E8532A'/></svg>)

# Snake

`1.0.0`

A grid snake with buffered turning, wall/wrap modes, a bonus pickup that times out, and speed that ramps with length.

game snake arcade

[Install](http://app.adi/marketplace/store/game-snake?install=1)[Download ADI](https://withadi.dev/#download)Opens in your panel, which asks where it goes Free — then this installs in one click

Already have ADI? [Install it from your panel](http://app.adi/marketplace/store/game-snake?install=1).

## What's included

As published. What actually installs is read off the pinned commit's own tree.

- **Dashboards**

  - game-snake — The game itself.

## About

## Snake

A grid snake, as one dashboard — buffered turning so a fast corner never gets dropped or flips the snake into itself, speed that ramps with length, an occasional bonus pickup that times out, a Wall/Wrap mode toggle, pause, and a best score kept per mode. Nothing on the backend: every rule lives in the browser.

This bundle carries one element:

| kind | name | what it is |
| --- | --- | --- |
| dashboard | `game-snake` | the game itself — frontend only, a one-route backend stub for the dashboard contract |

Install it:

```
adi-mono marketplace install <marketplace>/game-snake
```

It arrives archived, same as every dashboard — start it from the Dashboards page, or:

```
adi-mono marketplace start <marketplace>/game-snake/dashboards/game-snake
```

Then open it by its hostname. No secret, no project, nothing else to configure.

### Why no backend logic

Every rule — movement, collision, growth, pickups, scoring — runs client-side; there is nothing for a server to compute or to keep private. The backend half of this dashboard exists only because the dashboard contract (`guides/dashboards.md`) needs one: it is the template's own unmodified `backend/index.ts`, answering `/api/health` so the page's status dot has something to poll, and nothing more. No route file was added.

### How it's built

`frontend/modules/game.ts` is the panel; `frontend/modules/.logic.ts` is the whole rulebook — pure functions over a plain grid, no DOM, no randomness that isn't passed in as an argument. The dot in its name keeps it off the shell's auto-mounted panel list (`guides/dashboards.md`'s module discovery lists every `modules/*.ts` that doesn't start with `.`); the panel reaches it at runtime as a same-origin `./.logic.js` import, which the frontend server resolves straight back to this file.

The rules:

- **movement**: a 20×20 grid. Each tick advances the head one cell in the current heading and

drops the tail — unless the move eats something, in which case the tail stays and the snake is one cell longer.

- **input buffering**: up to two turns may queue ahead of the one being applied. A turn is only

ever checked against the *last already-queued* direction (or the current heading, if the queue is empty), so a fast "down then left" to round a corner lands both turns in order — and a turn that would reverse into the snake's own neck is rejected at the moment it's queued, never silently applied two ticks later into a death that looks like a dropped input.

- **collision**: running the head into its own body is fatal. Running it into the cell the tail is

about to vacate is **not** — the tail moves away the same tick, unless this move also grows the snake, in which case the tail stays and the same move is lethal.

- **wall vs wrap**: `wall` mode kills the snake against the board edge; `wrap` mode carries the

head to the opposite edge instead. A segmented control at the top switches modes (starting a fresh game), and each mode keeps its own best score.

- **growth and scoring**: the regular pickup is worth 10 and always respawns somewhere the snake

and the bonus (if one is live) aren't. Eating it has a chance of also spawning a bonus pickup worth 50, which disappears on its own after a fixed number of ticks if nobody gets to it.

- **difficulty curve**: tick speed ramps up with the snake's length (not with score or elapsed

time), floored so a very long run stays controllable rather than becoming unplayable.

- **pause**: Space or P, or the on-screen Pause button — the overlay offers Resume as well as New

game, and nothing ticks while it's up.

`frontend/modules/game.ts` renders this onto a board of real DOM elements kept alive across ticks — every element is keyed by its *index* in the snake array rather than by a stable per- segment id, which is what makes the whole body visibly crawl forward on a CSS `left`/`top` transition with no special-casing: a non-growing move produces `[nextHead, ...snake.slice(0, -1)]`, so index *i > 0*'s value after the move is exactly index *i - 1*'s value before it, and just re-reading each index's new position off the updated array animates correctly. A growing move only ever adds one element at index 0; nothing is removed mid-run. The bonus pickup draws its own remaining-life as a shrinking ring. The board is drawn as CSS percentages off the `COLS`/`ROWS` constants, so it holds together at any panel width, and the same palette is used in light and dark (the template's own ADI tokens already cover both — see `[data-theme="light"]` in `frontend/ index.html`).

Input: `ArrowLeft/Right/Up/Down` and `a`/`d`/`w`/`s` (case-insensitive, ignored while any text input or textarea elsewhere on the page has focus), a touch swipe (20px minimum, whichever axis moved further wins ties), and Space/P for pause. Best score is `localStorage["adi.gameSnake.best.<mode>"]`, one key per mode, scoped to the browser — there is nothing server-side to lose it with.

### Checking it works

```
bun test test/game-logic.test.ts
```

31 checks against `frontend/modules/.logic.ts` directly — no DOM, no server: movement in all four directions, wall-mode and self collision, the tail-vacate exception (and its growth-cancels-it counterpart), wrap-mode edge wrapping, growth and scoring on both the regular and bonus pickup, food/bonus never spawning on an occupied cell, the bonus pickup expiring on its own tick, the length-based speed ramp, and the input queue — duplicate and reversing turns rejected at enqueue time, the two-deep cap, and FIFO order on dequeue.

A second, optional check covers what the logic tests structurally can't: whether the pause/ game-over overlay is actually invisible when it should be. `.snake__overlay` sets `display: flex` (to centre its card), and an author stylesheet rule always outranks the UA's own `[hidden] { display: none }` at equal specificity — so a missing `.snake__overlay[hidden]` override renders the overlay permanently, over the board, regardless of the `hidden` attribute. Neither `bun:test`'s DOM nor happy-dom compute CSS cascade, so this needs a real browser:

```
bun add -d playwright && bunx playwright install chromium # one-time, not needed to play the game
bun test test/overlay-visibility.test.ts
```

It skips (with a warning, not a failure) if Playwright isn't installed — the dashboard itself has no dependency on it.

### Licence

MIT.

## Where it comes from

Installing clones this repository at this commit. A publisher who pushes something else afterwards changes nothing about what you already read.

- **Repository** — [github.com/adi-family/mp-game-snake](https://github.com/adi-family/mp-game-snake.git)
- **Commit** — `b4bd2350d6a651f8b6b1f09cac083622a465d7eb`
- **Branch** — `main`
- **Address** — `store/game-snake`
- **Contains** — dashboard
- **Manifest** — [raw.githubusercontent.com/adi-family/marketplace/main/apps/marketplace.json](https://raw.githubusercontent.com/adi-family/marketplace/main/apps/marketplace.json)

---
[ADI](https://withadi.dev/)

- [Download](https://withadi.dev/#download)
- [Store](https://withadi.dev/store/)
- [Docs](https://docs.withadi.dev/)
- [Source](https://github.com/adi-family/mono)
- [Licence](https://github.com/adi-family/mono/blob/main/LICENSE)
- [Changelog](https://github.com/adi-family/mono/blob/main/CHANGELOG.md)
- [Security](https://withadi.dev/security)
- [Privacy](https://withadi.dev/privacy)
- [Red team](https://withadi.dev/red-team-engagements)
- [Terms](https://withadi.dev/terms)
- [Support](https://withadi.dev/support)
- [Startups](https://withadi.dev/startups)
- [Partners](https://withadi.dev/#partners)
- [Partner portal](https://partners.withadi.dev/)
- [info@withadi.dev](mailto:info@withadi.dev)
- [Contact](mailto:info@withadi.dev)
