Type a name, get a 128x128 pixel-art cover: a subject, a lit stage, a palette and native bitmap lettering.
Made for the CHGame handheld's cart covers, and free for any website that needs covers or avatars.
Table of contents
Pocket Cover turns a title such as Star Patrol into a finished 128x128
pixel-art cover. The title is the seed. The same title, style and variant
always give the same pixels, so a site can store a name and regenerate its
cover whenever it needs it, without keeping an image. Different titles give
different subjects, palettes, scenery and lettering, and each title has an
endless run of variants.
It began as the cover-art generator for carts on the CHGame handheld. Every cover fits that console's cover format: 128x128 pixels, at most 11 custom colours plus CHGame's 4 fixed colours, all exact in RGB565. The same output works as a game cover, a profile avatar, a placeholder thumbnail, or anywhere else a small, unique, deterministic image helps.
There are no image assets. Every subject and background is drawn from geometry and sampled materials at generation time. The only authored pixel tables are the title fonts.
- 12 styles: mascots, spaceships, strange machines, relics, alien botany, floating islands, buildings, vehicles, planets, heraldry, glyphs and dungeon dioramas.
- A 15-colour, CHGame-safe palette per cover: 11 custom colours built in
OKLCh from a mood (night, complementary, analogous, dusk, and rare infernal,
toxic, neon, monochrome, sepia and pastel looks), plus black, cream, grey
and red. Every colour decodes exactly in RGB565, and the animated rainbow
key
#FF00FFis never emitted. - A title stack in native bitmap fonts: four pixel fonts, drawn at their own pixel grid and never scaled, with treatments that suit the mood.
- A lit stage: outlines and drop shadows from the subject's own mask, ground shadows, lamps, beams and depth layers, and varied framing that fits each subject to the space under the title.
- Wildness tiers: most variants stay canonical, and a seeded deck deals rarer, wilder looks among each title's variants.
- Deterministic: no clock, no
Math.random(). The same inputs give the same bytes on every platform. - Zero dependencies, plain ES modules, in the browser, Node 18+ and web
workers.
generateCoverneeds no DOM. - Small: about 130 KB of source gzipped for all twelve styles, and
createGeneratorlets a host ship only the styles it uses.
Each image shows variants 0 to 11 of one title, at native size.
Plain ES modules, no build step. Node is used only for the tests, contact sheets and tools.
Install from npm, or straight from git:
npm install pocket-cover
npm install github:bateske/PocketCoverDraw a cover on a canvas, in a plain page with no bundler:
<canvas id="cover" width="128" height="128" style="width:256px;image-rendering:pixelated"></canvas>
<script type="module">
import {drawCover} from './node_modules/pocket-cover/src/index.js';
drawCover(document.getElementById('cover'), 'Star Patrol', {style: 'spaceships', variant: 3});
</script>Or generate the pixels yourself, in Node, a worker or a browser:
import {generateCover, styles} from 'pocket-cover';
const cover = generateCover('Star Patrol', {style: 'spaceships', variant: 3});
cover.indices; // Uint8Array(128*128): palette index per pixel
cover.palette; // Uint32Array(15): 0xRRGGBB colours
cover.pixels; // Uint32Array(128*128): 0xRRGGBB per pixel (null with rgb:false)
styles.map(s => s.id); // ['mascot', 'spaceships', ...]Ship only the styles you need:
import {createGenerator} from 'pocket-cover/engine';
import spaceships from 'pocket-cover/recipes/spaceships';
import planets from 'pocket-cover/recipes/planets';
const {generateCover, drawCover, styles} = createGenerator([spaceships, planets]);Note
The output depends on the engine version. Record the package version with any seed you share; a saved PNG never changes.
| Export | What it does |
|---|---|
generateCover(title, options?) |
Returns a cover object (below). No DOM needed. |
drawCover(canvas, title, options?) |
Sets the canvas to 128x128, paints the cover, returns the same object. |
styles |
Frozen list of {id, label, revision, features, specializedFeatures}. |
createGenerator(recipes, {defaultStyle}?) |
Builds {generateCover, drawCover, styles} from chosen recipes. |
VERSION, SIZE |
Engine version (19) and cover size (128). |
Subpaths: pocket-cover/engine, /raster, /masks, /materials,
/geometry, /variation and /recipes/<id> for writing or trimming
recipes; see docs/FEATURES.md.
Arguments. title is 1-31 printable ASCII characters and not blank.
Outer spaces are trimmed and runs of spaces collapse. Case changes the seed;
the printed title is upper case.
| Option | Default | Meaning |
|---|---|---|
style |
'mascot' |
One of the style ids above. Unknown ids throw. |
variant |
0 |
Any nonnegative safe integer. |
framing |
'varied' |
'classic' keeps a fixed viewport; with mascot it reproduces the original v8 covers byte for byte. |
diagnostics |
false |
Adds cleanup candidates, the subject layer, title mask and stage data to quality. |
rgb |
true |
false skips the pixels array when you only need indices and palette. |
Result. width, height, pixels, indices, palette, the
normalised title, variant, style, version, styleVersion,
framing, composition, traits (subject description, including its
archetype), titleLines, titleBottom, titleExtent, look (mood,
tier, wildness, treatment and hues, or null for the classic mascot) and
quality.
- 128x128 pixels, one byte per pixel in
indices. - 15 palette entries. The 11 custom colours sit at indices 0-3, 5-7 and
9-12; the fixed CHGame colours are black (4), cream
#FFF4D6(8), grey (13) and red (14). Every colour is exact in RGB565, and the animated rainbow key#FF00FF(slot 15) and its near neighbours never appear. (The classicmascotframing keeps its original v8 palette.) - Exporting for a cart: download the native 128x128 PNG from the studio
(or encode
cover.pixelsyourself;scripts/png.mjsis a minimal encoder), then hand it to CHGame's cover tooling, which packs the palette and converts to RGB565. Never export a scaled-up image.
- Seed: the style, recipe revision, title and variant are hashed into independent random streams for the subject, look, scenery and layout.
- Look: a wildness tier, a mood, a title treatment and the 15-colour palette.
- Title: the title is laid out in a bitmap font, which reserves the top of the cover.
- Subject: the style's recipe draws geometry once into a recording; the engine measures it and replays it at its final scale in the space left.
- Effects: outlines, drop shadows and edge shading are derived from the subject's own fill mask, in exact device pixels.
- Stage: background scenery, depth layers, ground shadows, lamps and foreground, then a frame pass and the title on top.
- Cleanup: only detached single pixels nobody asked for are removed; marked details such as eyes, stars and glints are kept.
docs/FEATURES.md lists the shared features, their consumers and memory costs, and what each recipe can trim. docs/PIXEL_ART_RULES.md records the project's pixel-art rules, with sources and exceptions.
npm startThen open http://127.0.0.1:4173 for the studio (demo/index.html): pick a
style and title, browse variants and download native PNGs. The infinite
scroller is at /demo/scroll.html.
Online: studio and scroller. No server needed: demo/standalone.html is the whole
studio in one offline file. Double-click it. Rebuild it after source changes
with npm run standalone.
npm test # node --test: the full suite
npm run examples # contact sheets and manifests into examples/ (git-ignored)
npm run standalone # rebuild demo/standalone.html (add -- --check to verify it)
npm run review # pixel review sheets and craft lint
npm run banner # rebuild docs/banner.pngnpm run digest and npm run compare compare the current engine with a
frozen engine-18 copy in baseline/engine18/. That copy is local and
git-ignored; without it these scripts say so and stop. More QA tools (bench,
size, diversity, sheets) are described in tools/qa/README.md.
Read AGENTS.md and docs/PIXEL_ART_RULES.md
before changing how anything is drawn.
The engine is written so that a style could one day run on the CHGame itself (a CH32X035: 48 MHz RISC-V, no FPU, about 50 KB of usable flash and 18 KB of RAM). Seeds, hashing and variation are integer-exact; effects come from binary masks; scenes and recipes are separately trimmable.
A fixed-point C port of the shared core plus one recipe is estimated at about 22–32 KB of flash and 16.5 KB of RAM in a dedicated app (4-bit framebuffer plus 1-bit masks), drawing a cover in roughly 40–200 ms. Several styles (about 4–6) could share one app; a small recipe interpreter reading recipes from the SD card would allow all of them. These are estimates from the JavaScript, not a measured port.
The code is released under the MIT License, copyright 2026 bateske.
Warning
The bitmap fonts in src/font.js are not covered by the MIT License. Two of
the four (fonts 0 and 1, TomAndJerry14x10 and MonoBold8x8) have
unclear redistribution terms. Round9x13 is under the SIL OFL 1.1 and
MinimalFont5x7's author allows free use. Read NOTICE.txt
before redistributing.
- Fonts: Kate Garcia (MinimalFont5x7), StePickford (TomAndJerry14x10), castpixel (MonoBold8x8) and heraldod (Round9x13).
- Pixel-art references cited in docs/PIXEL_ART_RULES.md: Saint11's pixel-art articles, Cure's Pixel Art Tutorial, SLYNYRD's Pixelblog, Surma's Ditherpunk, Müller et al.'s Procedural Modeling of Buildings and Michael Azzi's Pixel Logic.
- Colour maths: Björn Ottosson's OKLab.
- CHGame, the handheld these covers were made for.












