A cozy, ad-free grocery-sorting puzzle that installs to your phone and plays fully offline — shipped as a single, dependency-free web file.

Top Shelf app icon — three matched groceries on a wooden shelf

At a glance

TypeBrowser game · Installable PWA
RoleSolo — design, engineering, art, level design
StackVanilla HTML/CSS/JS · Canvas-free SVG · Service Worker · zero dependencies
ScopeOne index.html (~153 KB, 2,847 lines) + service worker + procedural icon pipeline
StatusComplete · 100 levels, all solver-verified
AuthorLukas Martinson
Livelux-username.github.io/top-shelf
LicenseSource-available, free to play, noncommercial (PolyForm Noncommercial 1.0.0)

What it is

Top Shelf started as a question: what does this genre look like when you strip out every dark pattern? The answer is a calm, humane puzzle — no ads, no in-app purchases, no lives, no fail states you didn’t opt into. Each shelf holds three slots; you drag groceries between shelves until every shelf holds three of the same item, which clears it and opens breathing room for the next move. It’s the “ball sort / water sort” genre, re-skinned as a calm trip through a corner store — ten themed departments, 100 hand-tuned levels, a generous optional timer, and unlimited retries on stable boards so a good plan always pays off.

It’s also an engineering exercise in radical simplicity: the entire game ships as a single ~153 KB index.html with zero runtime dependencies, installs as a PWA, and runs with no network connection. Even the icons are generated procedurally by a from-scratch PNG/ICO pipeline. Every one of the 100 levels is proven solvable by an automated test harness before it ships.

Gameplay — sorting kiwis, watermelon and apples across wooden shelves

Why it’s interesting

A single self-contained file

The entire game — engine, 100 levels, art, styles, animation, audio, install logic — lives in one index.html. No build step, no bundler, no framework, zero runtime dependencies (the only external request is two Google Fonts, runtime-cached for offline use). You can read the whole thing top to bottom, drag it onto any static host, or open it straight off disk.

Installs like an app, plays with no network

A hand-written service worker caches the app shell on first load, so after one visit the game runs fully offline and installs to a phone home screen (Add to Home Screen on iOS, Install prompt on Android) — fullscreen, no browser chrome.

100 levels, every one provably solvable

Levels aren’t shipped on faith. A Node test harness evaluates the game’s pure engine functions, runs the solver against every generated board, and fails loudly on any level that isn’t solvable — or any generator that’s too slow. “Beatable and fast” is a test gate, not a hope.

Difficulty that respects the player

Three timer modes — Casual (no clock at all), Easy, and Hard — plus stable boards and unlimited retries. The design goal is flow, not punishment: the timer is a flavor of challenge you opt into, never a fail state forced on you.

Layered mechanics across departments

Settings and level select — timer modes, sound/music, the department grid and mechanic legend

The 100 levels are grouped into themed “departments” that gradually introduce new shelf mechanics — reserved slots, wildcard items, dispensers, shutters, and frozen tiles — so complexity ramps without a tutorial wall.

A procedural, dependency-free art pipeline

The app icons and browser favicons are generated by code, not exported from a design tool. A small Node script (tests/gen-icon.js) includes a tiny hand-rolled PNG encoder (CRC32 + zlib deflate, single IDAT chunk — no image library), a signed-distance-field renderer that draws the scene mathematically (rounded-rect shelf, gloss highlights, contact shadows, wood grain) with 3× supersampling for clean anti-aliased edges, and a hand-written .ico container that packs the 16/32/48 px favicons into one multi-resolution file (PNG-in-ICO) — again, no tooling. One node tests/gen-icon.js run regenerates every icon and favicon.


Tech notes

  • Frontend: Vanilla JavaScript, semantic HTML, hand-authored CSS. Grocery art is an inline SVG <symbol> sprite (crisp at any size, themable via currentColor).
  • Offline / install: Service Worker (cache-first app shell, runtime-cached fonts) + Web App Manifest with any and maskable icon purposes.
  • Audio: Optional sound effects and an ambient music loop, both toggleable.
  • Testing: Node harness that solves all 100 levels and times the generator.
  • Tooling: None required. No node_modules. The repo is the deliverable.

Tags: Game Dev · PWA · Vanilla JS · Procedural Graphics · Offline-first · Zero Dependencies · Solo Project