Proje Defteri

GIF to OLED Animation Converter

Drop an animated GIF and get every frame as a C array plus a working Arduino sketch for SSD1306, SH1106 and other monochrome displays. The flash cost is worked out as you go, so you find out the animation is too big here rather than at the end of a compile.

1. Choose an animation

Drop a GIF here or click to browse. Separate PNG/JPG frames also work.

2. Display settings

3. Frames

Click a frame to drop it from the animation. Removing every other frame is the quickest way to halve the flash cost when the memory bar turns red.

4. Output

How an OLED animation actually works

There is no animation support in any of the common display libraries. An SSD1306 has no frame buffer of its own beyond the single screen it is showing, and no concept of a timeline. What you call an animation is your sketch drawing one still bitmap, waiting, drawing the next one, and repeating. Everything this tool produces serves that loop: a list of bitmaps and the delay between them.

That framing explains the two things people get stuck on. The first is memory, because every frame is stored in full, uncompressed. The second is speed, because pushing a frame to the panel takes real time on the I2C bus and that time sets your ceiling on frames per second.

Working out the memory cost before you compile

A monochrome frame costs one bit per pixel, so a full screen 128x64 animation is 128 × 64 ÷ 8 = 1024 bytes per frame. The arithmetic from there is unforgiving:

An Arduino Uno has 32 KB of flash and the bootloader takes about 0.5 KB of it. Adafruit_SSD1306 with Adafruit_GFX costs roughly another 10 KB once you use text. That leaves something near 20 KB for your animation and the rest of your program, which is why the panel on this page warns well before the theoretical limit.

The single most effective saving is not fewer frames, it is a smaller drawing area. Halving both dimensions quarters the cost. A pair of animated robot eyes rarely needs the full panel: a 64x32 region placed in the middle of the screen looks the same and costs a quarter as much.

Note also that the arrays must be marked PROGMEM. Without it the compiler copies them into SRAM, and an Uno has 2 KB of that in total, so even two full frames will not fit. The checkbox above is on by default and the generated sketch reads the frames back with pgm_read_ptr(), which is the matching half of that decision.

Frame rate and the I2C bottleneck

A 128x64 frame is 1024 bytes, and I2C at the default 100 kHz moves roughly 10 KB per second once the protocol overhead is counted. That works out at about 90 ms per frame, or 11 frames per second, before your own code has done anything. If your animation looks slower than the GIF did, this is usually why rather than the delay you set.

Raising the bus with Wire.setClock(400000) takes it to roughly 40 frames per second, which is past what the eye needs. Most SSD1306 modules handle 400 kHz without complaint; if you see corruption, your pull-up resistors or wire length are the limit rather than the controller. An SPI display avoids the question entirely, since SPI runs an order of magnitude faster.

Choosing the right output format

The same pixels can be packed three different ways and picking the wrong one produces a recognisable failure, which makes them easy to diagnose.

Adafruit GFX stores pixels row by row, eight per byte, with the most significant bit on the left. Use it with display.drawBitmap(). This is the default and the right answer for most projects.

U8g2 XBM uses the same row layout but reverses the bit order inside each byte, because that is what the X11 bitmap format did. Use it with u8g2.drawXBMP(). If your image comes out mirrored in horizontal blocks of eight pixels, you have picked the wrong one of these two.

Raw page format matches how the SSD1306 controller stores its own memory: vertical pages of eight rows, one byte per column. It lets you memcpy a frame straight into the library's buffer and skip the per-pixel drawing loop, which is noticeably faster. The cost is that it only works at position 0,0 at the panel's exact size. If your picture appears sliced into horizontal stripes, you have used a vertical array where a horizontal one was expected.

Getting a good looking 1 bit image

A monochrome panel has no grey. Every pixel is on or off, so a photographic source has to be reduced, and how you reduce it matters more than the source resolution.

Hard threshold keeps everything above the cutoff and discards the rest. It is the right choice for line art, logos, text and pixel art, which is most of what ends up on these displays. Adjust the threshold slider until the shape reads clearly.

Floyd-Steinberg spreads the error from each pixel into its neighbours, producing the fine grain that makes photographs readable. On an animation it has one drawback: the grain changes completely between frames, which the eye reads as noise. If your animation looks like it is boiling, switch to a hard threshold.

Bayer uses a fixed 4x4 matrix, so the pattern stays still between frames. That makes it the better dithering choice for animation specifically, even though a single still frame usually looks better under Floyd-Steinberg.

Frequently asked questions

Why does image2cpp only give me one frame?

Because it draws the file into an HTML canvas and a canvas holds a single frame. That is a property of the browser, not a bug in image2cpp. This page parses the GIF container directly, walking the LZW compressed blocks, the disposal methods and the delay for each frame, so it recovers the whole animation. For still images image2cpp remains the tool to use, and it also covers colour output for TFT panels.

My animation is too big for an Arduino Uno. What now?

In order of how much they save: shrink the drawing area, drop every other frame, then shorten the loop. A 64x32 animation costs a quarter of a full screen one. If none of that is enough, an ESP32 or a Nano ESP32 has megabytes of flash and the question disappears. Storing frames on an SD card and streaming them is possible but usually slower than the display can draw anyway.

Can I use this for robot eyes?

Yes, and it is the most common use of this page. Draw or find a GIF of the blink and the look-around, keep the drawing area small, and use a hard threshold rather than dithering so the shapes stay crisp. Two 64x32 eye animations of ten frames each fit an Uno comfortably at 2.5 KB per set.

Does the tool support SH1106 or Nokia 5110?

Yes. The byte layout is what matters, not the controller. An SH1106 takes the same formats as an SSD1306 and only differs by a column offset that the library handles. A Nokia 5110 at 84x48 works too: set the size and use the raw page format, which is that controller's native layout as well.

What frame rate should I aim for?

Twelve to fifteen frames per second is enough for character animation and is roughly what an I2C bus at 400 kHz sustains alongside other work. Below eight it starts to read as a slideshow. Going above twenty is rarely worth the flash it costs, since each extra frame is another full bitmap.

Is anything uploaded?

No. Decoding, dithering, packing and code generation all run in your browser. The page works offline once loaded.