pxl : pixel art en tant que code
Vous écrivez un petit fichier .px.js ; pxl rend des PNGs et des aperçus agrandis que vous pouvez consulter. Le DSL
est conçu pour éviter d'écrire deux fois les pixels : miroir, contour automatique, variantes par échange de palette, pièces
réutilisables, primitives, pinceaux et tilemaps. Nécessite Node ≥ 22.18. Aucune installation requise.
PXL ci-après signifie node <répertoire de cette compétence>/bin/pxl.js.
Flux de travail
- Écrivez
art.px.js(commencez par le modèle ci-dessous ; des exemples dansexamples/). - Construisez uniquement ce sur quoi vous travaillez :
PXL build art.px.js --only slime,heart - Regardez-le : lisez
out/_preview/<nom>.png(agrandi, damier = transparent, grille tous les pixels, plus sombre tous les 8). Pour des corrections exactes de pixels, utilisezPXL ascii art.px.js <nom>(touches + règles x/y) ; c'est moins coûteux qu'une image. - Corrigez avec de petites modifications (un patch
grid(x, y, ...), une opération) et reconstruisez. Répétez. - Si l'utilisateur veut regarder : démarrez
PXL serve art.px.jsen arrière-plan et donnez-lui l'URL. Il reconstruit à chaque sauvegarde et rejoue les étapes de peinture de l'asset modifié ; il écrit également les mêmes fichiersout/, alors continuez à lire les aperçus comme d'habitude.
Les erreurs nomment le fichier:ligne:col, le chemin de l'asset/couche/opération et suggèrent des corrections ("vouliez-vous dire 'r' ?"). Un asset cassé n'empêche pas les autres de se construire.
Modèle
pal({ k: "#1b1630", g: "#8ccf63", G: "#4c8a44", t: "#2a5560", w: "#fff6e0", r: "#e2585e", R: "#a63a4c" })
ramps("ktGg", "kRr") // foncé -> clair, conduit shade()/light()
// Symétrique : écrivez la moitié gauche, mettez-la en miroir, tracez le contour.
sprite("slime", { mx: true, outline: "k" }, `
....ggg
..ggGGg
.ggGwGg
gggkwgg
gggkkgg
tgggggg
.tttttt`)
sprite("slime_red", { base: "slime", swap: { g: "r", G: "R" } }) // variante : 1 ligne
sprite("_eye", "wk\nkk") // "_" = partie, non exportée
sprite("chest", { w: 16, h: 12, outline: "k" },
rect(0, 3, 16, 9, "R"), rect(0, 0, 16, 4, "r"), stamp("_eye", 7, 4), shade([0, 9, 16, 3]))
scene("meadow", { w: 160, h: 96, bg: "w" },
layer("ground", { y: 64 }, rect(0, 0, 160, 32, "G"), noise(0, 0, 160, 32, "g", 0.1, 3)),
layer("props", stamp("slime", 40, 54), stamp("slime_red", 70, 54, { flip: "x" })),
layer("shadow", { shade: true }, poly([38, 64, 88, 64, 92, 67, 34, 67], "k"))) // ombre portée
sheet("chars", ["slime*", "chest"], { pad: 1 })
Essentiels
- Couleurs : touches d'un caractère de
pal,"#hex","."ignore,"_"efface. La palette par défaut est pico8 (0-f) ; utilisezuse("sweetie16" | "db16" | "endesga32" | "gray8")pour d'autres. - Les éléments s'exécutent dans l'ordre : chaînes de grille (dessinées à 0,0), opérations, fonctions
(c) => { c.rect(...) },layer(...). Puis miroir → échange → retournement → rotation → contour (grandit de 1px de chaque côté) → recadrage. - Formes :
px rect box line(…, width) hline vline circ ring ellipse poly fill wire text. - Tons :
dither grad noise,shade/light(region, steps, density),recolor, etshade:/light:pour les ombres portées et les lueurs. Régions :[cx,cy,r],[x,y,w,h], polygone, ou une fonction.clip(region, ...items)restreint tous les éléments (demi-formes, pentes). - Réutilisation :
stamp tile pattern tilemap scatter. Les noms peuvent être des groupes ou des globs (group("rocks", ["rock_a", "rock_a", "rock_b"]),"tree_*"). Chaque placement choisit aléatoirement mais de manière reproductible. Utilisezrflip,rswap,seed, etreseed: truepour de la variété par copie. - Pinceaux :
scatter("_leaf*", x, y, w, h, 30, { on: "g", clip: true, rflip: "xy" })peint une texture avec de petites pièces de sprite, seulement sur les couleurs données. - Animation :
anim("cat", { outline: "k", fps: 10 }, grid, frame(), frame(grid(23, 0, "_K"))): les images sont des patchs sur des éléments partagés ou unebase; ouframes: 6+(c) => ... c.phase ...pour des boucles procédurales. Estamper une animation anime la scène (rdelay: truedésynchronise les copies,frame: nen fige une). Vérifiez_preview/<nom>.png(toutes les images côte à côte) oupxl asciipar image. - Lueur :
glow("zgQ", { r: 6, strength: 0.5 })directement dans un corps sprite/scène donne à chaque pixel de ces touches un halo (flammes, potions, lunes, néon) ;ramp: 2reste dans la palette à la place. Donnez aux objets lumineux leurs propres touches pour que rien d'autre ne s'allume, ou limitez avecwhere.
API complète avec toutes les options : reference.md. Éclairage dynamique (lampes, lumière bloquée par des couches solides, lumière du ciel, style Terraria) : lisez lighting.md uniquement lorsqu'une scène a besoin de lumières.
Habitudes qui économisent des jetons et donnent un meilleur art
- Écrivez la moitié de tout ce qui est symétrique (
mx) et laissezoutlinetracer les contours. Ne dessinez jamais l'un ou l'autre à la main. - Les recolorations sont
base+swap; les détails répétés sont_parts+stamp; les familles sont ungroupou une boucle :for (let i = 1; i <= 3; i++) sprite("tree_" + i, ...). - Grandes zones : primitives,
grad,noise,pattern,tilemap. Jamais de grandes grilles à la main. - Texture organique (feuilles, herbe, pierres, rouille) : 2–3 minuscules pièces de pinceau +
scatteravecon/clip. - Pas de bruit de remplissage. Ne saupoudrez pas de bruit sur les murs, les sols ou le plâtre pour les rendre
"moins plats" : cela se lit comme des points aléatoires, pas comme un matériau. Chaque marque sur une surface a besoin d'une cause que vous pourriez
nommer, et elle doit se trouver là où cette cause la mettrait :
- structure construite : lignes de bardage/planches, joints de brique ou de pierre décalés, joints de carrelage ;
- intempéries : traînées de pluie sous les rebords et les appuis, saleté d'éclaboussure qui s'estompe depuis le sol, coulures de rouille sous les supports métalliques, peinture qui s'écaille des planches (suivant les lignes de la planche) ;
- encombrement qui raconte une histoire : affiches, tuyaux, bouches d'aération, fissures partant d'un coin.
La couleur unie est correcte ; un mur propre est meilleur qu'un mur pointillé.
noisen'est OK que là où le matériau lui-même est granuleux (gravier, sable, asphalte, toits en goudron, pierre rugueuse), et gardez-le clairsemé. Sur les visages dessinés à un angle (vues 3/4), faites en sorte que les marques suivent l'inclinaison du visage.
- Éclairage : définissez
ramps, puislight/shade; superposez un passagedensity: 0.5pour des bords doux. Utilisez des couchesshade: trueavec unpolypour les ombres portées sur de nombreux objets. - Utilisez des touches mnémotechniques (
g/Gvert clair/foncé). Gardez 2–4 nuances par matériau pour un rendu pixel art propre. - Construisez avec
--onlypendant l'itération ; une construction complète écrit également tous les aperçus.