# nittygrid (AT3000) — Full Documentation Bundle Source index: https://docs.nittygrid.com/docs/llms.txt Auto-generated tables (formats, SVG sanitization rules, render_code API, and built-in generators) are derived directly from the app itself, so they always match it. # Welcome to nittygrid > Design, preview, and export LED matrix animations on standard pixel grids or custom SVG layouts—with optional background image alignment, AI generation, and offline exports. **nittygrid** is a browser tool for making animations for LED displays. You describe what you want (or draw it), see it play on a preview of your display, and export it for your hardware or as a video. An **LED matrix** is simply a display made of many small lights (LEDs) in a grid, like a tiny screen. Yours can be a plain rectangle (for example 16×16 lights) or any custom shape, like a car light bar, drawn as an SVG file. > [!IMPORTANT] **Your projects live in your browser.** Nothing is uploaded to an account yet. If you clear your browser data or switch computers, your projects are gone unless you downloaded a **`.ngproj`** file first. Optional accounts and cloud sync are planned. ## How it works 1. **Set up your display** Pick a grid size (such as `16×16`) or upload an SVG where each shape is one LED. You can also import a photo of your product and place the LEDs on top of it, so the preview looks like the real thing. 2. **Create an animation** Each animation is a block on the canvas called an **animation node**. Make one by asking the AI, picking a built-in effect, writing a little JavaScript, painting pixels, drawing a path, or mapping a video. 3. **Connect and export** Wire your nodes from `Start` to `End` to set the playback order. Then export a `.ngrender.zip` for LED controllers or an `.mp4` video for presentations. ## Where to go next - [File & Export Formats](/docs/formats) — What gets saved where, and which file to use for what. - [SVG Matrices & Layouts](/docs/svg-matrices) — How to prepare an SVG for a custom-shaped display. - [Animations & render_code](/docs/animations) — How animations work, and the full JavaScript API. - [Main Product Tools](/docs/tools) — A tour of every tool in the studio. ## Four ideas to remember - **Recipe vs. frames.** Inside a project, an animation is stored as a small *recipe* (settings or code), so you can always tweak it. Only when you export does it become a fixed list of colors per frame. - **Position vs. playback.** Where a node sits on the canvas is just for your own tidiness. Playback follows the wires from `Start` to `End`. - **One coordinate system.** Every animation draws on a simple grid starting at the top-left `(0, 0)`. Custom SVG layouts are converted to this once, so effects work on any display shape. - **Built to last.** A `.ngrender.zip` contains plain JSON, the exact `layout.svg`, schemas, and a written spec. You can read it decades from now without nittygrid. ## For AI agents and scripts Every page here is plain Markdown, with tables generated from the app itself, so the details always match the app. - **[`/docs/llms.txt`](/docs/llms.txt)** — short index of all pages with raw `.md` links. - **[`/docs/llms-full.txt`](/docs/llms-full.txt)** — all pages in one plain-text file, including every table and the `render_code` API. - **Raw Markdown per page:** add `.md` to any URL (for example [`/docs/formats.md`](/docs/formats.md)), or use **Copy Markdown** at the top of a page. --- # File & Export Formats > How nittygrid saves your work in the browser today, how to back up projects (.ngproj) and recipes (.ngrecipe), and how rendered exports (.ngrender.zip, .ngrender.json, MP4) work. Before the file types, one important thing: **where does your work live while you create?** ## How Project Persistence Works Today (Browser-Only) nittygrid has no accounts or cloud storage yet. Everything is saved **in your browser, on this device**. - **Autosave.** Every change (animation nodes, wires, chat history, background alignment) is written to your browser's `localStorage` 300 ms after your last edit. Matrix setup presets live in `IndexedDB`. - **Tied to this browser.** A different browser, a private window, or another computer starts empty. Clearing your site data erases projects you have not downloaded. - **Videos can expire.** A video you loaded but did not bake only exists until you close the tab. Click **Bake to pixel animation**, or download a **`.ngproj`** while the tab is open, to keep it. > [!NOTE] **Coming later: Optional Accounts & Cloud Sync.** Optional accounts and cloud storage are planned. Downloadable `.ngproj` files and offline exports will keep working, so you never depend on our servers. --- ## Two kinds of files - **Editable files (`.ngproj`, `.ngrecipe`).** They keep your animations as recipes, so you can open and change them later. Use them for backups, moving computers, or sharing. - **Rendered files (`.ngrender.zip`, `.ngrender.json`, `.mp4`).** They contain the finished result as colors per frame, or as a video. Use them to play the animation on hardware or in other software, without nittygrid. ## Which file do I need? - [Save or share a whole project → `.ngproj`](#1-editable-project-bundles-ngproj) — The full canvas, matrix setup, background photo, and every node's history in one ZIP. - [Reuse one animation elsewhere → `.ngrecipe`](#2-reusable-recipe-bundles-ngrecipe) — One editable animation with its source matrix and media. - [Drive LEDs or archive for the long term → `.ngrender.zip`](#3-rendered-rgba-exports-nittygrid-animation-v1-0) — Exact colors per LED, timing, the SVG layout, schemas, and a written spec. - [Share a preview → `.mp4`](#4-mp4-video-export-mp4) — A normal video in the LED look, optionally on top of your product photo. --- ## 1. Editable Project Bundles (`.ngproj`) Download a project from the **Projects** overlay to get a **`.ngproj`** file. Older `.at3proj` files still import. > [!NOTE] A `.ngproj` is an ordinary **ZIP archive**. Rename it to `.zip` to look inside. ```text .ngproj (ZIP archive) ├── manifest.json Format ID ("at3proj"), schemaVersion (2), and asset inventory ├── project.json Matrix setup, animation recipes, canvas graph & chat history ├── notes.md Human-readable project summary ├── thumbnail.png|jpg|webp Project preview thumbnail (optional) ├── background/image.png|jpg|webp Aligned product/vehicle background photo (optional) ├── matrix/layout.svg Standalone sanitized SVG hardware layout (when using an SVG matrix) └── assets/session-1.mp4|webm|... Embedded video/image assets used by animations (optional) ``` **Good to know** - **No broken links.** Temporary browser links (`blob:`, `data:`) are replaced by real files inside the ZIP, referenced like this: ```json { "__at3AssetRef": "background/image.png", "mimeType": "image/png" } ``` - **Canvas and chat are kept (schema v2).** `project.json` stores node positions, the `Start` → `End` wires, and each node's chat and dated versions. Older schema v1 projects import as unconnected nodes, because nittygrid will not guess a playback order. - **SVG in both forms.** `project.json` keeps the complete matrix, including its SVG code. `matrix/layout.svg` is an identical standalone copy for other SVG tools. Import reads and cleans the JSON copy; changing only the standalone file does not change the project. - **Safe import.** Every restored SVG is cleaned the same way as an uploaded one. Missing referenced background images, thumbnails, or session media stop import instead of silently losing content. --- ## 2. Reusable Recipe Bundles (`.ngrecipe`) Export a single animation as a **`.ngrecipe`** to reuse it in another project. Older `.at3anim` files still import. ```text .ngrecipe (ZIP archive) ├── manifest.json format: "at3anim", schemaVersion: 1, entry: "animation.json" ├── animation.json Label, timestamp, AgentAnimation recipe & source matrixConfig ├── notes.md ├── preview.png|jpg|webp Still preview when available (optional) ├── matrix/layout.svg Source SVG layout snapshot when saved from an SVG matrix (optional) └── assets/session-1.* Embedded session media (optional) ``` A recipe stores the generator or `render_code`, not fixed-size pixels, so most recipes adapt to the new project's width `W` and height `H`. If the recipe includes its source matrix (`matrixContext: "saved-with-recipe"`), nittygrid compares it with your current project on import and warns about differences. It never overwrites your project's matrix. SVG matrices use the same storage rule as projects: `animation.json` keeps the SVG code and LED metadata, and `matrix/layout.svg` is an identical copy for use elsewhere. Import reads the matrix from the JSON. Older recipes with only the JSON copy still import. --- ## 3. Rendered RGBA Exports (`nittygrid-animation` v1.0) A hardware controller or script should not need to run JavaScript or know nittygrid's generators. It only needs to know **which LEDs exist, how fast frames advance, and the color of each LED in each frame**. That is exactly what this format contains. > [!IMPORTANT] The `nittygrid-animation` (`"version": "1.0"`) format and the self-contained **`.ngrender.zip`** (or a lone **`.ngrender.json`**) are designed to be readable offline, even if nittygrid no longer exists. The full specification is also at [`/formats/animation/1.0/FORMAT.md`](/formats/animation/1.0/FORMAT.md). 1. **One color model** Every LED in every frame is 4 bytes: `R, G, B, A`, each `0`–`255`. Monochrome uses the same layout with `R = G = B`. Bytes are written exactly as rendered, with no gamma change and no premultiplied alpha (`rgba8`, `srgb`, `straight`). 2. **Two ways to name LEDs** - **Regular grid (`layout.type: "grid"`):** just `rows`, `cols`, and `order: "row-major"`. LED IDs (`r0c0`, `r0c1`, ...) are derived from the top-left, so the file stays small. - **Irregular or SVG layout (`layout.type: "explicit"`):** only the real LEDs are listed in `layout.pixels`, such as `[{ "id": "a", "row": 0, "col": 0 }, ...]`. Gaps never create fake black pixels. 3. **Shapes stay in `layout.svg`** The JSON holds colors and timing only. Outlines live in the companion `layout.svg`, linked by `layout.reference.sha256`. 4. **Frames are Base64** Each entry in `frames.data` is one full frame in standard RFC 4648 Base64. Its decoded length is always `ledCount * 4` bytes. ### What is inside a `.ngrender.zip` Unzip it with any tool. The JSON is self-describing, and `README.txt` and `FORMAT.md` include a short Python 3 example for reading it. ```text .ngrender.zip ├── README.txt Plain-text offline decoding guide + short Python 3 teaching example ├── manifest.json Package format ("nittygrid-animation-package" v1.0) & SHA-256 checksums ├── animation.nganim.json The rendered RGBA frames, timing, and LED mapping ├── preview.apng Full-color LED-look animated PNG preview for instant viewing ├── FORMAT.md Bundled offline copy of the v1.0 format specification ├── animation.schema.json JSON Schema for animation.nganim.json ├── manifest.schema.json JSON Schema for manifest.json ├── LICENSE-MIT.txt & LICENSE-CC0.txt Free-license notices for the bundled schemas and specification └── layout.svg Included for SVG matrices; matches layout.reference.sha256 ``` ### Example: regular 2×2 grid (`layout.type: "grid"`) Two frames at 20 FPS (50 ms each, 100 ms loop). Each Base64 string decodes to `2 × 2 × 4 = 16` bytes in `r0c0`, `r0c1`, `r1c0`, `r1c1` order: ```json { "format": "nittygrid-animation", "version": "1.0", "meta": { "name": "Four LEDs", "exporter": { "name": "nittygrid", "version": "0.10.0-alpha.1" }, "exportedAt": "2026-10-17T10:00:00Z" }, "layout": { "type": "grid", "rows": 2, "cols": 2, "order": "row-major" }, "timing": { "mode": "fixed", "fps": 20, "loop": true }, "frames": { "pixelFormat": "rgba8", "colorSpace": "srgb", "alphaMode": "straight", "encoding": "base64", "data": [ "/wAA/wD/AP8AAP///////w==", "/////wAA//8A/wD//wAA/w==" ] } } ``` ### Example: irregular or SVG layout (`layout.type: "explicit"`) Three real LEDs (`a`, `b`, `c`) over two rows, with a gap in the middle of row 0. Each frame decodes to `3 × 4 = 12` bytes. No fake LED is written for the gap: ```json { "format": "nittygrid-animation", "version": "1.0", "layout": { "type": "explicit", "reference": { "path": "layout.svg", "sha256": "ede530d5b43f5d15bdde4b7b99af5559a9c753971ae98048db88effbba1a4ba4" }, "pixels": [ { "id": "a", "row": 0, "col": 0 }, { "id": "b", "row": 0, "col": 2 }, { "id": "c", "row": 1, "col": 1 } ] }, "timing": { "mode": "fixed", "fps": 20, "loop": true }, "frames": { "pixelFormat": "rgba8", "colorSpace": "srgb", "alphaMode": "straight", "encoding": "base64", "data": [ "/wAA/wD/AP8AAP//", "/////wAA//8A/wD/" ] } } ``` --- ## 4. MP4 Video Export (`.mp4`) The video looks like the studio preview, including round or square LEDs, SVG shapes, and optional monochrome. On browsers with a supported video encoder, frames are calculated and encoded one at a time with fixed timestamps. A slow export takes longer to finish, but the resulting video's duration still follows the selected FPS. H.264 MP4 is preferred; when only VP9 is available, the download uses `.webm`. Older browsers use the existing real-time recorder with a limited frame buffer, so timing can vary slightly. > [!TIP] Turn on **Include preview image** in the Export panel to place your aligned background photo under the glowing LEDs. The video uses the photo's size, rounded to even numbers as H.264 requires. --- ## Export memory and cancellation Portable JSON and ZIP exports process each frame immediately and retain only the actual LEDs' encoded color values. They do not collect a complete rectangular image for every frame. The file data and ZIP/APNG work still need memory. Use **Cancel export** to stop a running export. If an export exceeds a resource budget, nittygrid shows a message instead of continuing to allocate larger buffers. Use a shorter clip, fewer frames per second, smaller sampling addresses, or a smaller video output as appropriate. Limits protect nittygrid's own buffers; they cannot measure or reserve the system's free RAM. Browser/encoder resources and animation-owned state use additional memory, and an exhausted system can still terminate a tab. Rendering and export currently run locally; optional server export is planned. --- ## Technical reference: all formats and limits Generated from the app's own export code, so it always matches what nittygrid writes and reads. This table is for technicians and AI agents. | File / Export | Format Identifier | Purpose | Key Rules & Budgets | | :--- | :--- | :--- | :--- | | `.ngproj` (legacy `.at3proj`) | ZIP archive (`at3proj`, schema v2; reads v1) | Editable project snapshot: matrix setup, `matrix/layout.svg`, background alignment, animation recipes, canvas graph (`nodes`/`edges`), and per-node chat/version history. | Embedded binary assets under `background/`, `assets/`, and `thumbnail.*` referenced via `__at3AssetRef`. SVG code stays in the JSON; `matrix/layout.svg` is a standalone copy. | | `.ngrecipe` (legacy `.at3anim`) | ZIP archive (`at3anim`, schema v1) | Reusable single-animation recipe (`animation.json`) with source `matrixConfig` (`matrixContext: "saved-with-recipe"`), optional `matrix/layout.svg` snapshot, still thumbnail, and embedded session media. | Compares saved matrix context on import without replacing project settings; media hydrated from `__at3AssetRef`. SVG code stays in the JSON with an optional standalone copy; still previews use `preview.*`. | | `.ngrender.zip` & `.ngrender.json` *(v1.0 spec)* | ZIP (`nittygrid-animation-package` `"1.0"`) + JSON (`nittygrid-animation` `"1.0"`) | Self-contained rendered RGBA animation for regular grids (`grid`) and irregular/SVG matrices (`explicit` LED ID order). | Required ZIP entries: `README.txt`, `animation.nganim.json`, `preview.apng`, `FORMAT.md`, `animation.schema.json`, `manifest.schema.json`, `LICENSE-MIT.txt`, `LICENSE-CC0.txt` (+ `manifest.json` and `layout.svg` for SVG matrices). Budgets: ≤ 1,000,000 LEDs, ≤ 36,000 frames, ≤ 64 MiB RGBA, ≤ 96 MiB JSON, ≤ 8 MiB SVG, ≤ 128 MiB expanded ZIP. | | `.mp4` Video | H.264 / MP4 (`video/mp4`) | Rendered matrix video in the configured LED look (regular grid or physical SVG shapes). | Fixed-timestamp, frame-by-frame encoding where supported (H.264 MP4 preferred, VP9 WebM fallback); older browsers use a real-time recorder. Video canvases: ≤ 8192 px per side and ≤ 8,294,400 pixels each. Encoded output: ≤ 64 MiB. Optional **Include preview image** composites the aligned background. | | Browser export working buffers | Local resource budgets; independent of file format versions | Render/encode one frame before producing more; keep only encoded LED data for portable outputs. | Up to four full-raster render buffers: ≤ 64 MiB combined. Legacy JSON and older-browser video frame retention: ≤ 64 MiB. Final file data, encoder/browser resources and generator-owned state use additional memory; free system RAM cannot be guaranteed. | --- # SVG Matrices & Layouts > How nittygrid maps animations onto custom-shaped displays drawn as SVG, how to import a background image in Matrix Setup, and how SVGs are kept safe offline. Real displays are rarely a neat rectangle. A car light bar curves, a wearable follows the body, a sign has gaps. nittygrid handles this by splitting the problem in two: - **Where animations draw:** a plain grid of cells, like a tiny screen. - **How your display looks:** an **SVG** file (a vector drawing) where each shape is one physical LED. Each frame, nittygrid reads the color of a grid cell and paints it onto the matching SVG shape. Effects never need to know your display's shape. --- ## Add a background image In **Matrix setup** you can import a photo or render of your product (PNG, JPG, WebP) behind your LEDs. This works for both regular grids and SVG layouts. - **Align by dragging.** Move and resize the LED overlay on the photo until it matches the real LEDs. Grids can be stretched freely (hold **Shift** on a corner to keep proportions). SVG layouts always keep their aspect ratio. - **Image scale vs. Preview zoom.** *Image scale* changes how big the photo is in the saved result. *Preview zoom* (or trackpad pinch) only magnifies your view while aligning. - **It is saved with your work.** The image and alignment are stored in matrix presets and in `.ngproj` files. An `.mp4` export can include it too (**Include preview image**). --- ## How to prepare an SVG SVG can contain almost anything. For LED layouts, **keep it simple**. > [!IMPORTANT] **Basic shapes, with clear addresses when order matters.** > - **One simple shape per LED.** Use ``, ``, ``, ``, or a clean closed ``. > - **Label rows and columns for precise control.** Without addresses, nittygrid sorts shapes by their center points, top to bottom and then left to right. SVG layer order does not define hardware wiring. > - **Avoid masks and effects.** Do not use ``, ``, ``, ``, or `` to form LEDs. In Illustrator, Figma, or Inkscape, turn them into plain shapes before exporting. 1. **Draw one shape per LED** Shapes inside ``, ``, ``, or `` are ignored. Standalone `` copies are rejected, so expand them into real shapes first. 2. **Optionally label rows and columns** Add `id="px-ROW-COL"` (or `data-row` and `data-col`) with numbers that start at `0` in the **top-left**: ```xml ``` 3. **Or upload it without labels** nittygrid sorts shapes by their center points: ascending `y`, then ascending `x`. It assigns the resulting list to consecutive grid cells. This is a spatial fallback, not a wiring order; add row and column labels when you need a specific mapping. 4. **Split cells (optional)** If one cell holds two separately wired LEDs, such as an upper and a lower triangle, mark them `data-part="0"` and `data-part="1"`. They stay two separate LEDs in exports. > [!NOTE] **Bottom-up files are fixed automatically.** If your SVG uses `id="led-r00-c000"` with row 0 at the **bottom**, nittygrid flips it to the standard top-left start on import and keeps the original name in `data-source-id`. --- ## Safety and cleanup SVG files are XML and could in theory contain scripts or links to the internet. nittygrid cleans every SVG you upload, restore, or import from a project file. - **No code runs.** Scripts and animations are removed. The layout is shown inside an isolated shadow root and never parsed as HTML, so its styles cannot change the app. - **Fully offline.** External images, fonts, and stylesheets are removed, so the layout looks the same years from now. - **Common editor exports just work.** Standard `DOCTYPE` lines, comments, and missing `xmlns` are fixed, and wrapper tags like Illustrator's `` are unwrapped. ### Technical reference: the exact rules Generated from the app's own SVG import code, so it always matches what nittygrid does. This is for technicians and AI agents. | Rule Category | Specification | | :--- | :--- | | **Eligible LED geometry (`SHAPE_TAGS`)** | ``, ``, ``, ``, ``, ``, `` | | **Preserved geometry attributes** | `d`, `points`, `x`, `y`, `width`, `height`, `cx`, `cy`, `r`, `rx`, `ry`, `x1`, `y1`, `x2`, `y2`, `transform`, `fill-rule`, `clip-rule`, `pathLength` | | **Excluded definition contexts & `` policy** | Shapes inside ``, ``, ``, ``, ``, or `` are presentation definitions, never LEDs. Standalone `` instances are rejected—expand instances into independent shapes before importing. | | **Canonical LED metadata & group inheritance** | `data-led-id` (stable portable LED identity), `id="px-ROW-COL"`, `data-row="ROW"`, `data-col="COL"`, optional `data-part="PART"` (0-based top-left origin). Logical addresses on ancestor `` or `` elements are inherited during normalization; unusable or duplicate metadata attributes are stripped cleanly on import instead of rejecting the file. | | **Untagged sampling order & identity** | Without sampling addresses, LEDs are sorted by center `cy`, then `cx` and sampled from consecutive grid cells. Addressed layouts sort by sampling `y`, then `x`. When a row/col/part address lands on a cell another LED already uses, explicit `data-x`/`data-y` addresses keep their cell and the other LED moves to a spare row below the grid. `data-parts-per-cell` counts only as a positive whole number. IDs remain paired with their source shapes; SVG element order is not a hardware wiring order. | | **Accepted import aliases & editor cleanup** | `id="led-rNN-cNNN"` (automatically flipped from bottom-left to top-left origin on import). Standard DOCTYPEs, XML comments, and CDATA markers are stripped; missing SVG namespaces are added; unsupported containers like Illustrator `` are unwrapped. | | **Shadow DOM isolation & removed active elements** | Layouts render inside an isolated shadow root imported from the XML parse (never re-parsed as HTML, and SVG `