pxl: arte pixelado como código
Escribes un archivo .px.js pequeño; pxl renderiza PNGs y previsualizaciones ampliadas que puedes ver. El DSL
está construido para evitar escribir píxeles dos veces: espejado, contorneado automático, variantes de intercambio de paleta, partes
reutilizables, primitivas, pinceles y mapas de tiles. Necesita Node ≥ 22.18. Sin paso de instalación.
PXL abajo significa node <directorio de esta habilidad>/bin/pxl.js.
Flujo de Trabajo
- Escribe
art.px.js(comienza desde la plantilla abajo; ejemplos enexamples/). - Construye solo lo que estás trabajando:
PXL build art.px.js --only slime,heart - Míralo: lee
out/_preview/<nombre>.png(ampliado, ajedrez = transparente, cuadrícula cada píxel, más oscura cada 8). Para correcciones de píxeles exactas usaPXL ascii art.px.js <nombre>(teclas + reglas x/y); es más barato que una imagen. - Corrige con pequeños cambios (un parche
grid(x, y, ...), una operación) y reconstruye. Repite. - Si el usuario quiere verlo: inicia
PXL serve art.px.jsen segundo plano y dale la URL. Reconstruye en cada guardado y reproduce los pasos de dibujo del recurso editado; también escribe los mismos archivosout/, así que sigue leyendo las previsualizaciones como siempre.
Los errores nombran el archivo:línea:columna, la ruta del recurso/capa/operación y sugieren correcciones ("¿quisiste decir 'r'?"). Un recurso roto no detiene la construcción de los demás.
Plantilla
pal({ k: "#1b1630", g: "#8ccf63", G: "#4c8a44", t: "#2a5560", w: "#fff6e0", r: "#e2585e", R: "#a63a4c" })
ramps("ktGg", "kRr") // oscuro -> claro, impulsa shade()/light()
// Simétrico: escribe la mitad izquierda, refléjala, contornéala.
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 línea
sprite("_eye", "wk\nkk") // "_" = parte, no exportada
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"))) // sombra proyectada
sheet("chars", ["slime*", "chest"], { pad: 1 })
Esenciales
- Colores: claves de un carácter desde
pal,"#hex","."salta,"_"borra. La paleta predeterminada es pico8 (0-f); usause("sweetie16" | "db16" | "endesga32" | "gray8")para otras. - Los elementos se ejecutan en orden: cadenas de cuadrícula (dibujadas en 0,0), operaciones, funciones
(c) => { c.rect(...) },layer(...)s. Luego espejado → intercambio → volteo → rotación → contorneado (crece 1px por cada lado) → recorte. - Formas:
px rect box line(…, width) hline vline circ ring ellipse poly fill wire text. - Tono:
dither grad noise,shade/light(region, steps, density),recolor, y capasshade:/light:para sombras proyectadas y brillos. Regiones:[cx,cy,r],[x,y,w,h], polígono, o una función.clip(region, ...items)restringe cualquier elemento (mitades de formas, pendientes). - Reutilización:
stamp tile pattern tilemap scatter. Los nombres pueden ser grupos o globs (group("rocks", ["rock_a", "rock_a", "rock_b"]),"tree_*"). Cada colocación elige aleatoriamente pero de manera reproducible. Usarflip,rswap,seedyreseed: truepara variedad por copia. - Pinceles:
scatter("_leaf*", x, y, w, h, 30, { on: "g", clip: true, rflip: "xy" })pinta textura con partes sprites pequeñas, solo sobre los colores dados. - Animación:
anim("cat", { outline: "k", fps: 10 }, grid, frame(), frame(grid(23, 0, "_K"))): los fotogramas son parches sobre elementos compartidos o unabase; oframes: 6+(c) => ... c.phase ...para bucles procedurales. Sellar una animación la anima en la escena (rdelay: truedesincroniza copias,frame: ncongela una). Revisa_preview/<nombre>.png(todos los fotogramas lado a lado) opxl asciipor fotograma. - Brillo:
glow("zgQ", { r: 6, strength: 0.5 })directamente en el cuerpo de un sprite/escena da a cada píxel de esas claves un halo (llamas, pociones, lunas, neón);ramp: 2en cambio se mantiene en la paleta. Dale a las cosas brillantes sus propias claves para que nada más se ilumine, o limítalo conwhere.
API completa con cada opción: reference.md. Iluminación dinámica (lámparas, luz bloqueada por capas sólidas, luz del cielo, estilo Terraria): lee lighting.md solo cuando una escena necesite luces.
Hábitos que ahorran tokens y dan mejor arte
- Escribe la mitad de cualquier cosa simétrica (
mx) y deja queoutlinedibuje los contornos. Nunca dibujes a mano ninguno. - Los recolorizados son
base+swap; los detalles repetidos son_parts+stamp; las familias son ungroupo un bucle:for (let i = 1; i <= 3; i++) sprite("tree_" + i, ...). - Áreas grandes: primitivas,
grad,noise,pattern,tilemap. Nunca cuadrículas grandes a mano. - Textura orgánica (hojas, césped, piedras, óxido): 2–3 partes de pincel pequeñas +
scatterconon/clip. - Sin ruido de relleno. No espolvorees
noisesobre paredes, pisos o yeso para hacerlos "menos planos": se lee como puntos aleatorios, no como material. Cada marca en una superficie necesita una causa que puedas nombrar, y debe colocarse donde esa causa la pondría:- estructura construida: líneas de revestimiento/tablas, ladrillos o piedras con juntas desplazadas, costuras de azulejos;
- desgaste climático: manchas de lluvia bajo alféizares y repisas, suciedad salpicada que se desvanece desde el suelo, óxido que corre debajo de soportes metálicos, pintura descascarándose de tablas (siguiendo las líneas de las tablas);
- desorden que cuenta la historia: carteles, tuberías, rejillas de ventilación, grietas desde una esquina.
El color plano está bien; una pared limpia supera a una llena de puntos.
noiseestá bien solo donde el material en sí es granular (grava, arena, asfalto, techos de alquitrán, piedra rugosa), y mantenlo escaso. En caras dibujadas en ángulo (vistas en 3/4), haz que las marcas sigan la inclinación de la cara.
- Iluminación: define
ramps, luegolight/shaderegiones; apila un pase dedensity: 0.5para bordes suaves. Usa capas conshade: truecon unpolypara sombras proyectadas sobre muchos objetos. - Usa claves mnemotécnicas (
g/Gverde claro/oscuro). Mantén 2–4 tonos por material para un aspecto de arte pixelado limpio. - Construye con
--onlymientras iteras; una construcción completa también escribe cada previsualización.