Turn a PNG, JPG, GIF or SVG into a byte array you can paste into code. Get an Arduino PROGMEM bitmap for an OLED, a U8g2 XBM, a MicroPython bytearray, an RGB565 array for a colour TFT, or the raw file bytes as C, Python, Java, C#, hex or Base64.
I built this because I kept switching between three tools: one for OLED bitmaps, one for Base64, and one for colour screens. Now it's one page.
PNG, JPG, GIF (first frame), WEBP, BMP and SVG. Drag it in, pick a file, or paste a screenshot with Ctrl+V.
128×64, 128×32, 72×40, 64×48, 96×16, 128×128 for SH1107, 240×240 for small TFTs, or any custom size up to 1024 pixels.
Threshold, brightness and contrast sliders, invert, and Floyd–Steinberg, Atkinson or Bayer dithering for photos.
A crisp, enlarged preview of every pixel, plus an OLED-style view in white, blue or yellow.
Adafruit GFX drawBitmap, U8g2 drawXBMP and SSD1306 page order, with PROGMEM and width and height defines.
framebuf MONO_HLSB and MONO_VLSB bytearrays, and RGB565 uint16_t arrays for TFT_eSPI.
The original file as a C array, Python bytes, Java or C# byte[], a hex string, Base64 or a data URI.
Tick one box and get a complete Arduino or MicroPython program for SSD1306, SH1106 or SH1107 that you can upload as it is.
Drop a PNG, JPG, GIF, WEBP, BMP or SVG on the converter, or paste one with Ctrl+V.
Pick your screen size, such as 128x64, then move the threshold slider or turn on dithering until the preview looks right.
Pick Adafruit GFX drawBitmap, U8g2 drawXBMP, SSD1306 page order, MicroPython framebuf, RGB565 or raw file bytes.
Copy the array or the full sketch, paste it into the Arduino IDE or Thonny, and upload it to your board.
A microcontroller can't open a PNG. It has no image decoder and usually no file system, so the picture has to live inside your sketch as numbers. That list of numbers is the byte array. How the numbers are laid out depends on the screen and the library, and getting the layout wrong is the reason most converted images look like static.
On a monochrome OLED such as the SSD1306 or SH1106, every pixel is either on or off. That's one bit, so one byte holds eight pixels. The converter first turns your image into pure black and white with a threshold: any pixel brighter than the threshold becomes a lit pixel. Then it packs the bits into bytes.
128 × 64 = 8,192 pixels. Eight pixels per byte gives 8,192 ÷ 8 = 1,024 bytes. A 128×32 bar display is 512 bytes, and a 72×40 screen is 360 bytes. When a row isn't a multiple of eight, it's padded: a 10-pixel-wide image still takes two bytes per row. If your array is a different length from what you expect, the canvas size is wrong.
Adafruit's drawBitmap() reads the image row by row. Each byte is eight pixels across, and the leftmost pixel is the highest bit (bit 7). An 8×8 hollow square is eight bytes:
// 8x8 outline, horizontal, MSB = left pixel
const unsigned char box[] PROGMEM = {
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF
};
display.drawBitmap(60, 28, box, 8, 8, SSD1306_WHITE);
The top and bottom rows are all on (0xFF). The middle rows only have the two edge pixels on, which is 10000001 in binary, or 0x81. MicroPython's framebuf.MONO_HLSB uses the same layout.
U8g2's drawXBMP() also reads rows, but the leftmost pixel is the lowest bit (bit 0). The hollow square happens to look the same, but a single pixel in the top-left corner is 0x80 for Adafruit and 0x01 for XBM. Feed an Adafruit array to U8g2 and every group of eight pixels comes out mirrored.
Inside the SSD1306 the screen memory is split into pages that are eight rows tall. Each byte is a column of eight pixels, with the top pixel in bit 0. A 128×64 screen has eight pages of 128 bytes. The Adafruit library keeps its buffer in this order too, so a full-screen frame in page order can be copied straight into display.getBuffer(). MicroPython calls this layout MONO_VLSB, and it's what the ssd1306 driver uses internally.
SH1106 modules look identical to SSD1306 ones, but the chip has 132 columns of memory and the glass shows the middle 128. If you write raw page data starting at column 0, the picture shifts two pixels and the right edge wraps. Libraries that support SH1106, such as U8g2 and Adafruit SH110X, add the offset for you. That's why the converter's sketch option uses those libraries when you pick SH1106.
An Arduino Uno has 32 KB of flash but only 2 KB of RAM. A single 1,024-byte frame would eat half the RAM, so the array is marked PROGMEM and stays in flash. ESP32, ESP8266 and RP2040 boards have far more memory, and PROGMEM is harmless there. The converter shows the byte count and warns you when an image won't fit on an Uno.
| Format | Use it with | Byte layout |
|---|---|---|
| Adafruit GFX drawBitmap | Adafruit_SSD1306, Adafruit_SH110X, any Adafruit GFX display | Rows, 8 pixels per byte, left pixel = bit 7 |
| U8g2 drawXBMP | U8g2 and U8glib on SSD1306, SH1106, SH1107 and more | Rows, 8 pixels per byte, left pixel = bit 0 |
| SSD1306 page order | Full-screen frames copied into the display buffer | Columns of 8 rows, top pixel = bit 0 |
| MicroPython MONO_HLSB | framebuf.FrameBuffer and oled.blit() | Same as Adafruit |
| MicroPython MONO_VLSB | framebuf.FrameBuffer, ssd1306 driver | Same as SSD1306 page order |
| RGB565 | TFT_eSPI pushImage(), ST7735, ST7789, ILI9341 | One 16-bit word per pixel: 5 bits red, 6 green, 5 blue |
| Raw file bytes | C, Python, Java, C#, web APIs, embedding files | The original PNG or JPG file, unchanged |
Converting an animated GIF or a video? A single bitmap converter only takes one frame. The OLED Animation Maker converts every frame and writes the playback loop, and the GIF to OLED guide walks through it.
Page-order data drawn with drawBitmap(). Switch the output to Adafruit GFX and convert again.
An Adafruit array drawn with U8g2's drawXBMP(). Pick the U8g2 XBM format instead.
Your artwork is dark on a white background. Tick Invert, or draw with SSD1306_BLACK on a filled screen. Don't do both.
The threshold is too high or too low, so every pixel is off. Watch the preview and move the slider. Also try I2C address 0x3D.
It's an SH1106 module running SSD1306 code. Use the SH1106 option in the sketch.
The array is in RAM on an Uno. Keep PROGMEM ticked and don't copy the frame into a RAM buffer.
Photos have too many mid-tones for a hard threshold. Raise the contrast and use Floyd–Steinberg or Atkinson dithering.
The numbers in drawBitmap() must match the converted size. Use the generated defines so they can't drift.
Want to check the result before you flash? Paste your frames into the online OLED simulator. For a longer walk-through with image2cpp settings, read image to byte array for Arduino OLED.
Drop in a picture, check the preview, and copy the bytes. It takes about a minute.
Open the converterIt is your picture written as a list of numbers in code. For a monochrome OLED each bit is one pixel, so one byte holds eight pixels. For a colour screen each pixel is usually two bytes in RGB565. For a file upload or an API it can also mean the original PNG or JPG bytes, unchanged.
128 times 64 is 8,192 pixels. Each pixel is one bit, and a byte holds eight bits, so 8,192 divided by 8 is 1,024 bytes. A 128x32 bitmap is 512 bytes.
Pick Adafruit GFX drawBitmap. It writes horizontal bytes with the leftmost pixel in the highest bit, which is what display.drawBitmap expects.
Pick U8g2 drawXBMP. U8g2 reads XBM bitmaps, which are horizontal bytes with the leftmost pixel in the lowest bit. An Adafruit array drawn with drawXBMP looks mirrored inside every group of eight pixels.
The byte order does not match the drawing function. Vertical page-order data drawn with drawBitmap gives stripes, and an Adafruit array drawn with drawXBMP looks scrambled. Convert again with the format that matches your library.
Yes. Choose Raw file bytes. The converter writes the original file as a Python bytes object, a Java or C# byte array, a C array, a hex string, Base64 or a data URI.
Yes. It covers the same Adafruit and horizontal formats as image2cpp and adds U8g2 XBM, MicroPython framebuf, RGB565, raw file bytes and complete sketches. For animated GIFs, use the OLED Animation Maker, which converts every frame and writes the playback loop.
No. The conversion runs in your browser with JavaScript, so the image never leaves your computer.
Related searches: image to byte array · image to byte array online · image to byte · image to c array · png to c array · jpg to c array · bmp to c array · bitmap converter · image2cpp alternative · image to hex · image to base64 · arduino bitmap · oled bitmap · PROGMEM bitmap · ssd1306 bitmap · sh1106 bitmap · u8g2 xbm converter · micropython framebuf image · rgb565 converter · image to bytes python · image to byte array c# · image to byte array java