Skip to content
uilianksPublic

About

No description, website, or topics provided.

Resources

Stars

64 stars

Watchers

1 watching

Forks

 
 

Repository files navigation

pxl

Pixel art as code. Write a compact .px.js file; get PNGs, sprite sheets with JSON, animated GIFs, and a live browser viewer that replays the painting step by step as you save.

Built for AI agents (it ships as a ready-made agent skill) and pleasant for people too. No runtime dependencies, no build step: just Node ≥ 22.18.

Animated witch's cottage at night: bubbling cauldron, fireplace, cat and frog

Comet Repair shop scene Gull & Gill bait shop scene Moon Buns bakery scene Neon noodle bar scene

Animated glowing cave Animated cat Animated frog Animated clock

Every image above is generated from a file in examples/.

This fork

A fork of crsolver/claude-pxl (all credit for pxl itself goes there) that adds tools aimed at one thing: art that reads as hand-made pixel art, not "shapes filled with a gradient". Part of a family with vex (vector art, skeletal animation), chirp (game audio), kiln (low-poly and voxel 3D) and loom (seamless textures and PBR materials).

The same mushroom before (black outline, pillow shading, lint flags it) and after (hue-shifted shades, selective outline, light from the top-left)

  • shades("1234", "#c8323c"): builds a hue-shifted ramp from one color. Shadows go cool and more saturated, lights go warm and paler, and the ramp is wired up for shade()/light().
  • selout: "k": a selective outline. Each outline pixel takes the color it touches, two ramp steps darker, so the line softens on lit edges and deepens in shadow.
  • pxl lint: an art critic for built pixels. It flags orphan pixels, jaggies (doubled corners in 1-px lines), pillow shading, too many colors and near-duplicate colors, each with coordinates and a _preview/<name>.lint.png overlay. Agents run it before they call a sprite done.
  • Annotate mode in the live viewer (✎ annotate or a): paint over an asset (markup colors point at things, palette colors mean "make it this color"), pin numbered notes to pixels, add a comment, and press send to Claude. It writes out/_feedback/<asset>.json (every painted pixel, every note with the pixel under it) and an annotated PNG, so you can say "check the feedback" instead of describing where the problem is.

The before/after is examples/polish.px.js: lint flags the left mushroom for pillow shading and passes the right one.

Pixel-art portrait of a turquoise Cthulhu-like creature with angry glowing red eyes and a beard of tentacles, in a pale ivory frame, a dark night sea behind it

The Watcher in the Frame (showcase/eldritch.px.js, 130×160) was directed with annotate mode: the creature is a small 3D model rendered into pixels (ellipsoids shaded from their normals with shades ramps, tentacles as tubes) and every revision after the first round came from painted marks and notes.

A pixel-art knight in plate armor with a red plume, sword and blue shield, standing in a beam of light from a stained-glass window in a ruined chapel, the background softly out of focus

The Vigil (showcase/knight.px.js, 384×216, so ×5 is 1920×1080) is a study in the HD-2D manner: a crisp sprite in a lit, depth-blurred world. The knight is built plate by plate from shaded solids on shades ramps, each part outlined with selout. The chapel is painted per pixel in true color: the stained-glass window's light is traced onto the flagstones and through the dust (the knight's shadow cuts the beam), then depth of field, bloom and a vignette.

The same knight in isometric view on a floating slab of 5x5 flagstones, his tile ringed in gold and the four neighboring tiles washed blue

The isometric take (showcase/knight_iso.px.js): the knight is a 3D model of signed distance fields ray-marched by a true 2:1 isometric camera, standing on a 5×5 slab of 56×28 tiles. His tile is ringed in gold like a tactics cursor, the four he could step to are washed blue, and his shadow is found by marching from each floor pixel toward the sun.

Quick start

git clone https://github.com/uilianks/pxl.git
cd claude-pxl
node bin/pxl.js serve examples/shop.px.js     # live viewer at http://127.0.0.1:4321/

Or run it without cloning:

npx github:uilianks/pxl build art.px.js

Write art.px.js:

pal({ k: "#1b1630", g: "#8ccf63", G: "#4c8a44", w: "#fff6e0", r: "#e2585e", R: "#a63a4c" })

// Write the left half; mirror it and outline it automatically.
sprite("slime", { mx: true, outline: "k" }, `
  ....ggg
  ..ggGGg
  .ggGwGg
  gggkwgg
  GGGGGGG`)
sprite("slime_red", { base: "slime", swap: { g: "r", G: "R" } })   // a recolor in one line

sheet("chars", ["slime*"], { pad: 1 })
node bin/pxl.js build art.px.js
out/sprites/slime.png  out/sprites/slime_red.png      the art, at 1x
out/chars.png          out/chars.json                 sprite sheet + TexturePacker-style JSON
out/_preview/*.png                                    upscaled previews with a pixel grid

--godot also writes a Godot 4 SpriteFrames .tres beside every sheet and anim strip (one animation per tag, with frame durations and loop), ready for an AnimatedSprite2D.

Examples

Each image is built by npm run examples from the file named under it (sources in examples/, images in docs/gallery/).

Sprites, recolors and a sheet: draw the left half, mirror it, outline it, then make variants by swapping colors. examples/basics.px.js

sprite("slime", { mx: true, outline: "k" }, `
  ....ggg
  ..ggGGg
  .ggGwGg
  .gggggg
  gggkwgg
  gggkkgg
  tgggggg
  .tttttt`)
sprite("slime_red", { base: "slime", swap: { g: "r", G: "o", t: "p" } })
sprite("slime_blue", { base: "slime", swap: { g: "b", G: "B", t: "n" } })
sprite("coin", { w: 10, h: 10 }, circ(4, 4, 4, "o"), circ(4, 4, 3, "y"), vline(4, 2, 5, "o"), px(3, 2, "w"))
sheet("chars", ["slime*", "heart", "coin", "chest"], { cols: 4, pad: 1 })

Sprite sheet: green, red, blue and winking slimes, a heart, a coin and a chest

A procedural animation, placed in a sprite: the flame's frames come from c.phase, so it loops seamlessly; the torch that stamps it is animated too. With --godot the build also writes anims/flame.tres, a SpriteFrames for Godot. examples/torch.px.js

anim("flame", { w: 9, h: 12, frames: 6, fps: 10 }, (c) => {
  const k = c.phase * Math.PI * 2, sway = Math.round(Math.sin(k) * 1.2)
  c.poly([0, 11, 4 + sway, Math.round(1 + Math.sin(k * 2)), 8, 11], "r")
  c.poly([2, 11, 4 + sway, Math.round(4 + Math.cos(k * 2)), 6, 11], "o")
  c.poly([3, 11, 4, Math.round(7 + Math.sin(k)), 5, 11], "y")
  c.px(4, 10, "w")
})
sprite("torch", { w: 13, h: 26, outline: "k" },
  rect(5, 12, 3, 12, "n"), rect(3, 11, 7, 2, "d"), vline(5, 13, 10, "s"), stamp("flame", 2, 0))

Animated wall torch with a flickering flame

Frames as patches: a cat drawn once, then each frame() changes only the tail. examples/no_magic.px.js

anim("cat", { outline: "k" }, `
  .......................sK.
  ........................K.
  .s...s..................K.
  .Ks.sKs........sssss...K..
  .KKKKKK......ssKKKKKs.Ks..
  KKKKKKKs...ssKKKKKKKKKK...
  KkYKKkYKsssKKKKKKKKKKKK...
  KKKKKKKKKKKKKKKKKKKKKKK...
  .KKqKKrKKKKKKKKKKKKKKKK...
  ..KKKKrKKKKKKKKKKKKKKKK...
  ...KKKaKKKKKKKKKKKKKKK....
  .KKKKK.KKKK...KKKKKKK.....
  KKKKK.KKK....KKKKKKK......`,
  frame({ ms: 300 }), frame({ ms: 300 }, grid(23, 0, "__K")), frame({ ms: 300 }, grid(23, 0, "_K")),
  frame({ ms: 300 }, grid(22, 0, "sK_")))

Animated black cat flicking its tail

The live viewer

pxl serve art.px.js opens a viewer that rebuilds on every save and replays the asset you edited op by op, so you (or whoever is watching an agent work) can see the art being painted. It also writes the same out/ files as build.

Commands

pxl build <art.px.js> [--out DIR] [--only a,b] [--scale N] [--no-preview] [--godot]
pxl serve <art.px.js> [--port 4321] [--out DIR | --no-out] [--no-preview]
pxl ascii <art.px.js> <name>     print an asset as palette keys with x/y rulers
pxl list  <art.px.js>            list assets, groups and sheets with source locations
pxl lint  <art.px.js> [name ...] critique pixels: orphans, jaggies, pillow shading, palette
pxl trace <image.png> [--w N] [--colors 16 | --palette pico8] [--dither 0.3]
                                 turn a PNG into an art file: extracted palette + grid, with its fidelity

pxl means node bin/pxl.js in a clone, or npx github:uilianks/pxl. Art files can be .js or .ts (types/pxl.d.ts gives editor completion).

What's in the DSL

  • Palettes and ramps: one-character color keys, built-in palettes (pico8, sweetie16, endesga32…), dark→light ramps that drive shade() / light().
  • Sprites: text grids, mirroring, auto-outline, palette-swap variants, flips/rotations, reusable _parts, stamp.
  • Primitives and brushes: rects, lines, circles, polygons, gradients, dithering, noise, a built-in pixel font, scatter brushes, tilemaps, random groups with reproducible seeds.
  • Scenes: layers, cast-shadow and light layers, per-layer PNG export.
  • Animation: frames as patches over a base sprite → strips + JSON tags + GIF.
  • Lighting (optional): lamps, glow, normal-based relief (see lighting.md).
  • Images in, pixel art out: image("photo.png", x, y, { w: 64, palette: "kgGw", dither: 0.3 }) draws any PNG (a photo, a reference, a kiln or vex render) quantized to the palette; pxl trace writes a whole art file from one. isobox draws 2:1 isometric blocks; rot takes any angle.
  • Errors point at file:line:col and the asset/layer/op path, with "did you mean…" hints.

Full API: reference.md. Agent workflow and essentials: SKILL.md.

Use it with an AI agent

This repository is an agent skill: SKILL.md teaches the workflow (write → build → look at the preview → patch), reference.md holds the full API, and lighting.md is loaded only when needed.

Claude Code: clone it into your skills folder, then ask Claude to draw something.

# all projects
git clone https://github.com/uilianks/pxl.git ~/.claude/skills/pxl-pixel-art
# or just one project
git clone https://github.com/uilianks/pxl.git .claude/skills/pxl-pixel-art

Other agents (Codex, Cursor, …): point them at SKILL.md and tell them the CLI is node <path to pxl>/bin/pxl.js.

Tip: ask the agent to start pxl serve so you can watch it paint in your browser.

Development

npm install        # dev only: typescript, @types/node
npm run check      # type check + tests + build every example and regenerate docs/gallery
npm run examples   # just the examples and the gallery
src/core/     engine, no Node APIs: image, palette + ramps, canvas ops, build, trace, project
src/node/     vm sandbox loader, PNG/GIF encoders, outputs, CLI, live server
src/viewer/   browser viewer (served type-stripped; no bundler)
types/        pxl.d.ts globals for editor support in .px.ts files
scripts/      build.js (dist/ for npm installs), examples.ts, gallery.ts
examples/     example art files
test/         node:test suites

In a clone the CLI runs the TypeScript sources directly. When installed as a package it runs dist/, which npm install / npm pack generate via scripts/build.js (Node won't strip types inside node_modules).

License

MIT. Copyright crsolver (original pxl); fork additions by uilianks under the same license.

Support

If this tool helps you, you can support its development on Ko-fi.

About

No description, website, or topics provided.

Resources

Stars

64 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages