Is my image uploaded?
No. The image is decoded and resampled with your browser's Canvas API, and the bytes are generated in the page. The image is not sent to a server.
Convert PNG, JPG, BMP or GIF images into C byte arrays for SSD1306/SH1106 OLEDs and TFT displays — 1-bit, RGB565 and grayscale — with a preview rendered from the bytes.
Loading Image to C Array…
Loading Image to C Array…
Putting a logo or an icon on a small display means turning an image into exactly the bytes your graphics library expects — and every library expects something slightly different. Adafruit GFX's drawBitmap() reads 1-bit rows left to right with the most significant bit first, padded to a whole byte. U8g2's XBM functions read the same rows least significant bit first. The SSD1306 controller's own memory is organised in vertical 8-pixel pages. Colour TFTs want 16-bit RGB565, and some drivers want those two bytes swapped. Get any of it wrong and the display shows noise, a mirror image, or a negative. This converter resamples your image to the display size with the fit mode you choose, applies rotation, flipping and a background colour for transparent pixels, converts to 1-bit with a threshold or one of three dithering methods (or to colour or grayscale), and writes a C array with the right type, optional PROGMEM and size #defines. The preview is decoded back from those generated bytes, so what you see is what the display will draw.
The image is decoded with createImageBitmap, which applies EXIF orientation, then drawn onto a canvas at the display size using contain, cover, stretch or the original size. Rotation and flipping are applied to the resampled pixels, and transparent areas are composited over the background colour you pick.
Pixels are converted to grayscale (Rec. 601 luma) and compared with the threshold. Floyd–Steinberg and Atkinson dithering diffuse the rounding error to neighbouring pixels, which keeps gradients and photos recognisable; ordered (Bayer 4×4) dithering produces a regular pattern that compresses and animates better.
Horizontal: byte = 8 pixels of a row, bit 7 = leftmost. XBM: bit 0 = leftmost. Vertical pages: byte = 8 pixels of a column within an 8-row page, bit 0 = top. RGB565: 5 bits red, 6 green, 5 blue in a uint16_t, optionally byte-swapped. The encoders and decoders are round-trip tested on random bitmaps.
The variable name is turned into a valid C identifier, arrays are emitted with hex or binary literals, and the byte count is checked against AVR limits: more than 2 KB without PROGMEM exceeds an ATmega328P's RAM, and more than 32 KB exceeds its flash. An example sketch for Adafruit_SSD1306 or TFT_eSPI is included.
Pick the 128×64 preset, 1-bit output and Horizontal (Adafruit GFX) byte order. Each row of 128 pixels becomes 16 bytes, most significant bit on the left, so the whole screen is 16 × 64 = 1,024 bytes — small enough for an ATmega328P's flash with PROGMEM, and far too big for its 2 KB of RAM without it.
Adafruit_SSD1306 draws that array with drawBitmap(x, y, array, width, height, color). The tool generates the array and #defines; the sketch below is the matching example it offers. If the image comes out as vertical stripes, you picked Vertical pages (the SSD1306's native GDDRAM layout, for writing straight into the display buffer) — switch back to Horizontal.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Paste the generated array here:
// #define LOGO_WIDTH 128
// #define LOGO_HEIGHT 64
// const uint8_t logo[] PROGMEM = { 0x00, 0x00, ... }; // 1,024 bytes
Adafruit_SSD1306 display(128, 64, &Wire, -1);
void setup() {
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // 0x3D on some 128x64 modules
for (;;) {}
}
display.clearDisplay();
display.drawBitmap(0, 0, logo, 128, 64, SSD1306_WHITE);
display.display();
}
void loop() {} Almost always a byte-layout mismatch, not a bad image. Diagonal smearing means the width in drawBitmap() doesn't match the array (a 120-pixel-wide image still occupies 15 whole bytes per row — pass the real width, not a rounded one). Mirrored 8-pixel blocks mean the library expects least-significant-bit-first rows (XBM, used by U8g2's drawXBM). An inverted image means your display draws 1 as off: use the Invert option or draw with SSD1306_BLACK on a white background.
Short answers for the things developers usually ask before trusting a tool.
No. The image is decoded and resampled with your browser's Canvas API, and the bytes are generated in the page. The image is not sent to a server.
For Adafruit GFX drawBitmap() on an SSD1306 or SH1106, use 1-bit horizontal. For U8g2 drawXBMP() or Adafruit drawXBitmap(), use XBM. To copy straight into an SSD1306 frame buffer, use vertical pages. For colour TFTs driven by TFT_eSPI or Adafruit ST77xx, use RGB565.
Inverted: by default bright pixels are "on" (lit on an OLED); turn on Invert for dark artwork on a light background. Scrambled or striped: the byte layout doesn't match the draw function — horizontal vs vertical, or MSB vs LSB first (XBM). The preview is drawn from the generated bytes, so it shows the same mistake the display would.
On AVR boards like the Uno and Nano, yes — without it the array is copied into the 2 KB of RAM at startup. drawBitmap() expects PROGMEM data there. On ESP32, RP2040 and STM32, const arrays already stay in flash, so PROGMEM is harmless but unnecessary.
In the horizontal and XBM formats each row is padded to a whole byte with zero bits, which is what drawBitmap() expects. In the vertical page format, a height that isn't a multiple of 8 pads the last page. The generated code includes a comment when padding is added.
Yes. The EXIF orientation that phone cameras write is applied when the image is decoded. Very large photos are first scaled down to 4096 pixels on the longest side, and animated GIFs use their first frame.
Useful follow-ups when one conversion usually turns into three more.
Flash ESP32, S2, S3, C3, C6 and H2 firmware from the browser with esptool-js: chip detection, offset presets, overlap and chip-mismatch checks, erase, and MD5 verification.
A serial monitor and plotter in the browser for Arduino, ESP32 and any USB-serial device: baud presets, DTR/RTS reset, text or hex, send, filter, timestamps and log export.
Convert between hex, decimal, binary and octal with two's complement and byte order, inspect IEEE 754 floats bit by bit, and convert byte lists between C, Python and C# syntax.
Want the background and worked examples? There's a longer write-up.
Read the Image to C Array guide