Skip to content

About

Procedural, deterministic 128x128 pixel-art cover and avatar generator for the web. Made for CHGame cover art.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

7 Commits

Folders and files

Repository files navigation

Pocket Cover: eight generated 128x128 covers, from a spaceship and a floating island to a mascot and a dungeon room

Procedural pixel-art covers and avatars, seeded by a title

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.

Release Stars Forks Issues MIT License

Platform Dependencies Size 128x128 Deterministic

Try the demo »

Studio · Scroller · Docs · Report a bug

Table of contents
  1. About
  2. Getting started
  3. API reference
  4. The CHGame cover contract
  5. How it works
  6. Running the demo locally
  7. Development
  8. On-device notes
  9. License
  10. Acknowledgments

About

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.

Features

  • 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 #FF00FF is 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. generateCover needs no DOM.
  • Small: about 130 KB of source gzipped for all twelve styles, and createGenerator lets a host ship only the styles it uses.

Style gallery

Each image shows variants 0 to 11 of one title, at native size.

Style id Subject Variants 0-11
mascot Friendly original characters in a landscape (the default) Mascot covers
spaceships Interceptors, fighters, freighters and explorers Spaceship covers
machines Tanks, gauges, pipes, gears, coils and dishes Machine covers
relics Swords, crystals, keys, amulets, grimoires, rings and staffs Relic covers
plants Pods, mushrooms, branching stems, flowers and toothed plants Plant covers
islands Floating dioramas with towers, peaks, trees and waterfalls Island covers
buildings Wizard towers, castles, temples, factories and spires Building covers
vehicles Cars, tanks, submarines and rovers Vehicle covers
planets Banded, oceanic and fractured worlds, rings and moons Planet covers
heraldry Shield fields, divisions, charges and ornaments Heraldry covers
glyphs Connected rune skeletons, terminals and halos Glyph covers
dungeons 2:1 cutaway rooms with doors, pools, bridges and treasure Dungeon covers
One cover from each of the twelve styles

Built with

JavaScript Node.js HTML5 Canvas

Plain ES modules, no build step. Node is used only for the tests, contact sheets and tools.

(back to top)

Getting started

Install from npm, or straight from git:

npm install pocket-cover
npm install github:bateske/PocketCover

Draw 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.

(back to top)

API reference

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.

(back to top)

The CHGame cover contract

  • 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 classic mascot framing keeps its original v8 palette.)
  • Exporting for a cart: download the native 128x128 PNG from the studio (or encode cover.pixels yourself; scripts/png.mjs is 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.

(back to top)

How it works

  1. Seed: the style, recipe revision, title and variant are hashed into independent random streams for the subject, look, scenery and layout.
  2. Look: a wildness tier, a mood, a title treatment and the 15-colour palette.
  3. Title: the title is laid out in a bitmap font, which reserves the top of the cover.
  4. 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.
  5. Effects: outlines, drop shadows and edge shading are derived from the subject's own fill mask, in exact device pixels.
  6. Stage: background scenery, depth layers, ground shadows, lamps and foreground, then a frame pass and the title on top.
  7. 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.

(back to top)

Running the demo locally

npm start

Then 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.

(back to top)

Development

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.png

npm 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.

(back to top)

On-device notes

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.

(back to top)

License

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.

(back to top)

Acknowledgments

(back to top)

About

Procedural, deterministic 128x128 pixel-art cover and avatar generator for the web. Made for CHGame cover art.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages