# How do you get a custom icon or animation onto a tiny display — without hand-encoding every byte?

> SSD1306 OLED, MAX7219 matrix and WS2812 displays each want pixel bytes packed a different way. Design it once in Pixel Studio and export a ready .h header instead.

[HTML version](https://soldr.ai/docs/pixel-studio-design-to-firmware)

[Docs](https://soldr.ai/docs) / Screens & dashboards

You want your build to show something specific — a logo on boot, a weather icon, a little walking animation — not just numbers scrolling past in Serial Monitor. The library your display uses wants that image as a byte array, packed a very particular way. Get one bit wrong and the screen doesn't error, it just shows noise.

3 min read
Screens & dashboards
OLED · LED matrix · WS2812

## What you're up against

An SSD1306 OLED wants a horizontal bitmap, one bit per pixel, packed eight pixels to a byte. A MAX7219 LED matrix wants one byte per row, per module, sent through a completely different call. A WS2812 strip or ring wants a 24-bit RGB value per LED, in the physical wiring order — which is almost never the same as reading order once you're on a ring or a serpentine matrix. Three displays, three incompatible byte layouts, and a single transposed bit produces a screen full of garbage with no error message pointing at the cause.

## Why this happens

None of this is a bug in your code — it's that pixel data and firmware bytes are two different representations of the same picture, and the translation between them depends on exactly which display family you're driving. Hand-authoring that translation once is tedious; doing it again for an animation with several frames, or after redesigning the icon, is where most people give up and ship a placeholder instead of what they actually wanted on screen.

## Designing it in Pixel Studio

- **Open Pixel Studio** From the app sidebar, or jump straight in with the `#pixel-studio` link. It's a full-screen editor, not a separate page — nothing you draw leaves your browser.

- **Pick your exact display** Seven presets, three families: SSD1306 OLED (0.96&Prime; 128×64, or 0.91&Prime; 128×32), MAX7219 LED matrix (8×8, or a 32×8 chain), and WS2812 addressable LEDs (an 8×8 matrix, a 16-LED ring, or an 8-LED strip). The canvas and export both match whichever one you pick — you're never guessing at pixel packing.

- **Draw the frame** Click or drag directly on the canvas, or use the Text, Icons, QR, or Image tabs on the right — Text sizes up to 3×, Icons come in 8×8 or 16×16, Image lets you upload a picture and dither or threshold it down to the display's resolution. WS2812 presets add a colour swatch row since those pixels are RGB, not just on/off.

- **Add frames for animation** The Timeline panel adds, duplicates, or deletes frames, each with its own duration (20ms–60s). Turn on onion-skin to see the previous frame as a faint guide while you draw the next one, then hit Play to preview the loop before exporting anything.

- **Watch the byte-count warning** The export panel shows a running byte count. On an SSD1306 export past roughly 4KB you'll see an amber warning — fine on an ESP32, but that's most of an Uno or Nano's flash gone on one image, so it's worth seeing before you flash, not after.

- **Download the header and drop it in** "Download .h" gives you a ready-to-include C header — the pixel bytes, a durations array if you animated, and a commented-out player snippet built on `millis()` rather than `delay()`, so it won't stall the rest of your sketch. "Copy" puts the same header text on your clipboard if you'd rather paste it straight into your IDE.

> [screenshot] Pixel Studio, OLED preset, a 16x16 thermometer icon placed at x=110 y=4, animation panel open showing 3 frames, the .h export button visible.

## Why this is deterministic, not generated

There's no model anywhere in this path. What you draw is what gets packed into bytes, pixel for pixel, by the same code every time — not an AI's interpretation of your design. That matters for a display: you want the exact icon you placed, not a plausible-looking approximation of it.

> **Fully client-side, zero credits** — Drawing, animating, and exporting in Pixel Studio never touches a server or spends credits — it's canvas and file APIs running in your browser, start to finish.

## A worked example

Say you're building a small status display for a weather station: an ESP32 driving a 0.96&Prime; SSD1306 OLED over I2C, showing a thermometer icon that blinks between two frames while a reading updates underneath it. Pick the 128×64 OLED preset, stamp the 16×16 thermometer icon near a corner, duplicate the frame with the icon shifted a pixel or two for a subtle blink, set each frame to around 500ms, and export. The header drops straight into a sketch alongside `Adafruit_SSD1306` — the bytes already match what `drawBitmap()` expects, so there's no manual conversion step between "I designed this" and "the board shows it."

| Part | Why it's there | Price |
|---|---|---|
| ESP32-DevKitC (Black) | runs the sketch, drives the OLED over I2C | ₹340 |
| 0.96&Prime; OLED 128×64, I2C (SSD1306) | the exact preset used in this example | ₹259 |
| Core of the example | the design work happens in the browser, not on the board | ₹599 |

## If it still doesn't look right on the board

- Double-check you exported for the same preset your wiring matches — an 8×8 WS2812 matrix header sent to a ring, or an OLED header sent to a MAX7219, will compile fine and still show nothing usable, because the byte layouts aren't interchangeable.

- On a phone or tablet, Pixel Studio shows a handoff QR instead of an export button, since a phone can't flash a board directly — scan it on your desktop to pick the design back up and export from there.

- If a QR code you're generating fails to place, the payload is probably over the 213-byte limit this editor can encode at display resolution — shorten the text or link first.

- Still not matching what you expected? Paste your sketch and describe what the display is actually showing into Soldr — that's a diagnosis, not a generation, so it doesn't cost credits.

[Previous Wiring & pins](https://soldr.ai/docs/wiring-and-pins)
