$ cd ..

Así codeamos con Claude · 05: export

10° · 48% · noche fresca
claudeclaude-codeprocesstinyspritedev-log

Hasta ahora lo que dibujábamos vivía dentro del editor y del localStorage. Cinco capítulos para llegar a poder sacarlo del browser. Acá está el botón export y arriba dibujé algo para probar.

Editor TinySprite con un personaje pixel-art pintado: un bichito violeta con sombrero y cuerpo rosa, en un sprite 16x16. La toolbar ahora muestra el botón export al lado de new
el bichito en cuestión, dibujado a 16×16 mezclando colores de la paleta cozy y la pico-8.

El modal

Botón export en la toolbar, hotkey ⌘E.

Botón export en hover mostrando keycap ⌘E y tooltip Exportar sprite
hover sobre export
Modal Exportar sprite con tabs PNG SVG JSON Sheet, scale 1x activo, filename test.png, info salida 16 x 16 px, botones Cancelar y Descargar
modal con formato, escala y filename

Cuatro formatos: PNG, SVG, JSON, sprite sheet. La escala (1×, 2×, 4×, 8×) aplica solo a PNG y sheet, que son raster. El filename se autogenera con el nombre del sprite + sufijo de scale (untitled@4x.png) y se actualiza si cambiás formato o escala. El input es editable si querés algo custom.

PNG: el problema del 1×

Un sprite 16×16 exportado a 1× son 16 píxeles de alto. En cualquier monitor moderno, eso es invisible. Cuando lo subís a Twitter, Discord, o lo abrís en el visor del sistema, el resultado es esto:

Captura de macOS Preview mostrando test.png a tamaño nativo: una manchita de pixels apenas visible en el centro del visor
1× en macOS Preview a tamaño nativo. el sprite es esa manchita en el medio.
El mismo test.png con el visor zoomeado al sprite, donde el personaje se ve grande y pixelado correctamente
el mismo archivo con zoom del visor.

Por eso ofrecemos hasta 8×. Para postear en redes lo lógico es 4× o 8×: lo suficientemente grande como para que se vea bien y la plataforma no lo escale mal. Por debajo del scaling, todo es imageSmoothingEnabled = false así los píxeles quedan crocantes en vez de borrosos.

SVG: pixel art como vectores

El SVG es la opción interesante: cada píxel es un <rect> en un viewBox de width × height. El navegador re-escala el archivo a cualquier tamaño sin pérdida.

El sprite del bichito abierto en el navegador, renderizado desde el SVG. Los píxeles se ven con bordes nítidos y el archivo se escala al viewport sin perder definición
el SVG en el browser: escalable sin pixelarse, perfecto para web.

El detalle de implementación: en vez de emitir un <rect> por cada píxel pintado (que para 16×16 puede ser hasta 256 rects), agrupo runs horizontales del mismo color por fila. Un sprite con áreas planas (lo que es casi todo pixel art) baja de cientos de rects a docenas. Para el bichito de arriba, el SVG son unos 25 rects en vez de los 132 píxeles no-transparentes.

JSON: el formato canónico

JSON es el mismo formato que usa el autosave (capítulo 4). Si exportás un sprite y después lo querés recuperar más adelante, esto es lo que vas a importar. Versionado con v: 1 así el día que el schema cambie el deserializer descarta lo viejo y arranca con defaults en vez de explotar.

Captura de un editor de código mostrando el contenido de test.json: campos v, sprite con id, name test, width y height 16, palette con valores numéricos, activeFrameIndex 0, frames array con id, durationMs 100, layers con id name visible opacity y pixels en base64
el JSON exportado. píxeles en base64, paleta como array de enteros packeados RGBA.

Los píxeles van en base64 y no como array de enteros plano. Sí: el JSON queda "feo" para abrir en un editor de texto, pero un sprite 128×128 pasa de ~330KB (como array) a ~88KB (base64). Para algo que vive en localStorage o que el usuario va a guardar miles de veces, el ahorro vale.

Sprite sheet: arquitectura lista, feature pendiente

El cuarto formato es sprite sheet: un PNG con todos los frames del sprite arreglados en una fila horizontal. Como en este capítulo todavía no hay animación, los sprites tienen un solo frame, y el sheet termina siendo idéntico al PNG normal.

Lo dejamos visible igual porque el código y el wiring ya están listos. El día que el editor pase a tener frames múltiples (02 · animación, en algún capítulo futuro), el sheet pasa automáticamente a ser una tira horizontal con un frame por columna, sin tocar el modal.

Próximo capítulo