Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Image to C Array (Arduino Displays)

Images as C arrays for OLED, e-paper and TFT screens, with a byte-exact preview.

Image No upload Works offline Free, no sign-up

Next steps

About the Image to C Array (Arduino Displays)

Turn a logo, icon or photo into a C/C++ array that a microcontroller can draw: monochrome bitmaps for SSD1306 and SH1106 OLEDs, Nokia 5110 and e-paper screens, 4-bit greyscale for SSD1327/SSD1322 grey OLEDs, and RGB565 or RGB888 colour for ST7735, ST7789 and ILI9341 TFTs.

Pick your display, choose threshold or dithering, and copy code that is ready for Adafruit GFX (drawBitmap, drawRGBBitmap), u8g2 (drawXBMP) or plain C with the byte layout you need — horizontal rows or SSD1306 vertical pages, MSB or LSB first, PROGMEM or not. The preview is decoded back from the generated bytes, so it shows exactly what your screen will show. Nothing is uploaded.

How to use it

  1. Choose, drop or paste an image. Logos and icons with clear shapes give the cleanest monochrome results.
  2. Pick your display (for example SSD1306 OLED 0.96″ — 128 × 64) or enter a custom width and height, then how the image fills it: fit, fill (crop) or stretch. Rotate or flip if your screen is mounted differently.
  3. Choose the pixel format. For monochrome, move the threshold or pick a dithering method for photos, and choose whether set bits are the light or the dark pixels.
  4. Choose the library: Adafruit GFX, u8g2 (XBM) or plain C with your own byte layout. Set the array name and PROGMEM.
  5. Check the preview, then Copy code or Download .h and include the file in your sketch. Download .bin saves the raw bytes, for example for an SD card or a flash file system.

Examples

A 128 × 64 logo for an SSD1306 OLED with Adafruit GFX
Input
logo.png · SSD1306 128 × 64 · Monochrome · Threshold 128 · Adafruit GFX
Result
#define LOGO_WIDTH 128
#define LOGO_HEIGHT 64
const uint8_t logo[] PROGMEM = { 0x00, 0x00, … }; // 1024 bytes

display.drawBitmap(0, 0, logo, LOGO_WIDTH, LOGO_HEIGHT, SSD1306_WHITE);

A 128 × 64 bitmap is 128 × 64 ÷ 8 = 1,024 bytes — half of the 2 KB of RAM on an Arduino Uno, which is why it is kept in flash with PROGMEM.

Colours in RGB565
Input
Red (255, 0, 0) · green (0, 255, 0) · blue (0, 0, 255) · white · mid grey (128, 128, 128)
Result
0xF800 · 0x07E0 · 0x001F · 0xFFFF · 0x8410

Red keeps 5 bits, green 6 and blue 5: (R5 << 11) | (G6 << 5) | B5. Each channel is rounded to the nearest level.

The same 8 pixels in three layouts
Input
One row: on, off, off, off, off, off, off, on
Result
Horizontal, MSB first (Adafruit GFX): 0x81 · horizontal, LSB first (XBM / u8g2): 0x81 · vertical pages, LSB first: eight bytes 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01

Symmetric patterns hide the bit order — test with an asymmetric image such as an arrow.

Which layout does my library expect?

  • Adafruit GFX, drawBitmap(): each row is a whole number of bytes, (width + 7) / 8, and the most significant bit is the leftmost pixel; set bits are drawn in the colour you pass. drawXBitmap() reads the same rows with the least significant bit first. Source: Adafruit_GFX.cpp.
  • u8g2, drawXBM() / drawXBMP(): XBM bitmaps, rows of whole bytes with the least significant bit as the leftmost pixel. drawXBMP reads arrays declared with U8X8_PROGMEM (u8g2 reference).
  • SSD1306 / SH1106 display RAM (choose Plain C, Vertical pages, LSB first): the screen is split into pages 8 pixels high; each byte is one column of a page with bit 0 at the top — the order of the controller’s graphic display RAM in the Solomon Systech SSD1306 datasheet, and of the Adafruit_SSD1306 frame buffer (buffer[x + (y / 8) * WIDTH]). Copying such an array straight into the buffer fills the screen.
  • Adafruit GFX, drawRGBBitmap(): one uint16_t RGB565 value per pixel, row by row. ST7735, ST7789 and ILI9341 controllers receive RGB565 over SPI as two bytes, high byte first — choose bytes, high byte first when you send the array to the display yourself.

How the colours are reduced

  • Grey is BT.709 luma of the sRGB values: Y = 0.2126 R + 0.7152 G + 0.0722 B (ITU-R BT.709-6).
  • Threshold: a pixel is light when its grey is at least the threshold (1–254). Auto picks the level with Otsu’s method: the one that separates the dark and light pixels most cleanly (largest variance between the two groups).
  • Floyd–Steinberg and Atkinson error diffusion turn grey tones into dot patterns — good for photos on monochrome screens. Bayer 4 × 4 / 8 × 8 ordered dithering gives a regular cross-hatch that stays stable when only part of the image changes. With dithering, the threshold shifts the balance between light and dark.
  • 4-bit grey: 16 levels (0 = black, 15 = white), two pixels per byte. RGB565 rounds red and blue to 32 levels and green to 64. The optional dithering spreads the rounding error so gradients show less banding.
  • Transparent areas and the bars left by Fit are filled with the background colour you choose before conversion.

Fitting images into small memories

  • Monochrome: width × height ÷ 8 bytes (rows padded to whole bytes). 128 × 64 → 1,024 bytes.
  • 4-bit grey: width × height ÷ 2. RGB565: width × height × 2 — a full 320 × 240 screen is 153,600 bytes. RGB888: × 3.
  • An Arduino Uno (ATmega328P) has 32 KB of flash and 2 KB of RAM (Arduino UNO R3 datasheet), so keep bitmaps in flash and colour images small. ESP32 and RP2040 boards have far more flash.
  • The size of the array is shown under the preview before you copy anything.

Limitations

  • Output bitmaps are limited to 2,048 pixels per side and 1 megapixel (1,048,576 pixels) in total, which covers panels such as 800 × 480 e-paper and 480 × 320 TFT screens.
  • One image becomes one array; animations and sprite sheets are not split into frames.
  • LVGL image descriptors, compressed (RLE) bitmaps and fonts are not generated.
  • Whether set bits appear light or dark depends on your library and display; check the preview colours and use Set bits are: dark pixels if your screen shows the image inverted.
  • Binary literals (0b10100000) need GCC or Clang (as in the Arduino, ESP-IDF and Pico toolchains) or C23 / C++14; choose hex for other compilers.

Privacy

Everything happens in your browser. What you enter or open here is not uploaded or stored by MySmartCoPilot.

Frequently asked questions

My image appears inverted on the screen. What do I change?

Switch Set bits are to the other option. With Adafruit GFX drawBitmap() set bits are drawn in the colour you pass: on an OLED that is usually white, so set bits should be the light pixels; on e-paper drawn in black, choose the dark pixels.

My logo comes out completely black (every bit is 0). Why?

Transparent areas are filled with the Background colour before conversion. A black logo on a transparent background therefore disappears on the default black background of the OLED presets. Choose a white background and set bits = dark pixels, so the logo becomes the set bits that the OLED lights up — the tool offers this as one click when it sees the problem.

The picture looks scrambled or split into stripes. Why?

The byte layout does not match what your code expects. Adafruit GFX drawBitmap() needs horizontal rows with MSB first, u8g2 drawXBMP() needs horizontal rows with LSB first, and copying straight into an SSD1306 frame buffer needs vertical pages with LSB first. Also check that the width and height in your code match the array.

Do I need PROGMEM?

On AVR boards such as the Arduino Uno, yes: without it the array is copied into the 2 KB of RAM at start-up. On the ESP8266 too: constant data such as strings is placed in RAM there unless it is marked PROGMEM (ESP8266 Arduino core: PROGMEM). On ESP32, RP2040 and other ARM boards constant arrays stay in flash anyway, and the keyword does no harm. Adafruit GFX drawBitmap() / drawRGBBitmap() and u8g2 drawXBMP() read PROGMEM arrays. With PROGMEM switched off, the Adafruit GFX code declares the array without const, because the library reads every const bitmap as PROGMEM data — on AVR boards a const array in RAM would be drawn as garbage.

What is the difference between RGB565 words and bytes?

Words (uint16_t) are what Adafruit GFX drawRGBBitmap() and many TFT libraries take. Bytes, high byte first, are the order the controller expects on the wire, for code that sends the data over SPI itself. Low byte first matches how a uint16_t sits in memory on little-endian microcontrollers.

Which dithering should I use?

For logos, text and icons use a plain threshold. For photos on a monochrome screen, Floyd–Steinberg keeps the most detail, Atkinson looks crisper with cleaner highlights, and Bayer gives a regular pattern.

Is my image uploaded?

No. The image is decoded and converted by your browser, and the code is generated on your device. The tool works offline once the page has loaded.

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.