Community project

Animated OLED Eyes

Arduino
Photo of Animated OLED Eyes
Generated with AI

Jack Romagnola

Last updated September 15, 2026

This project brings a pair of animated eyes to life on a small OLED display, complete with realistic pupil movement and blinking. The eyes follow a smooth animation cycle, looking left and right while occasionally blinking, creating an engaging character display that responds to a simple animation loop.

Built around an Arduino Uno and a compact 0.66-inch OLED screen, this guide provides everything needed to assemble and program the project. Readers will receive a complete wiring diagram, parts list, and ready-to-upload firmware that handles all the eye animation logic, along with step-by-step assembly instructions to get the eyes animating in minutes.

Wiring diagram

Wiring diagram for Animated OLED Eyes

Gather all the parts

QtyComponent
1

Grove OLED Display 0.66" (SSD1306)

0.96 in, 128×64, 3.3–5 V I²C

0.66" 64x48 monochrome OLED on a 20x20 mm Grove module using the SSD1306 controller over I2C. Ultra-small form factor; 3.3V/5V compatible, configurable I2C address.

Assemble it in 4 steps

1. Place the Nano and screen

With the Nano unplugged, put the Nano and the small four-pin OLED screen on a breadboard or non-metal table. Find the screen pins labelled VCC, GND, SDA, and SCL.

  • The screen must be a 0.96-inch, 128×64 I²C SSD1306 module marked 3.3–5 V or 5 V compatible.
  • Do not move wires while the Nano is powered, because a misplaced wire can short the power pins.

2. Connect the screen power wires

Connect OLED VCC to Nano 5V (power). Connect OLED GND to Nano GND (ground). These two wires give the 3.3–5 V compatible screen power.

  • Use a red wire for VCC and a black wire for GND if you have them.
  • Make sure VCC and GND are not swapped — swapped power can damage the screen.

3. Connect the screen data wires

Connect OLED SDA to Nano A4 (data). Connect OLED SCL to Nano A5 (data). These two wires carry the drawing commands that make the eyes move.

  • A4 and A5 are printed beside the Nano's analogue-pin header.
  • Do not connect the OLED SDA or SCL pin straight to 5V or GND.

4. Start the eyes

Check the four screen wires, plug the Nano into USB, then press Deploy in Schematik. The eyes continuously glance left, centre, and right, with an occasional double blink.

  • If the screen stays blank, first check that SDA and SCL have not been swapped.
  • Only use the direct screen wiring shown here with an OLED marked 3.3–5 V or 5 V compatible.

Review all connections

1. Connections between "oled_1" and "Arduino"

Functionoled_1Arduino
groundGNDGND
i2cSDAGPIO 18
i2cSCLGPIO 19
powerVCC5V

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

void drawEye(int centerX, int pupilX, bool blinking);
void drawFace();

const uint8_t SCREEN_WIDTH = 128;
const uint8_t SCREEN_HEIGHT = 64;
const uint8_t OLED_ADDRESS = 0x3C;
const int I2C_SDA_PIN = 18;  // Nano A4
const int I2C_SCL_PIN = 19;  // Nano A5
const unsigned long FRAME_INTERVAL_MS = 50UL;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
unsigned long lastFrameMs = 0;
uint16_t frameNumber = 0;

void drawEye(int centerX, int pupilX, bool blinking) {
  if (blinking) {
    display.drawFastHLine(centerX - 23, 32, 47, SSD1306_WHITE);
    return;
  }

  display.fillRoundRect(centerX - 24, 16, 48, 34, 15, SSD1306_WHITE);
  display.fillCircle(pupilX, 33, 9, SSD1306_BLACK);
  display.fillCircle(pupilX - 3, 29, 2, SSD1306_WHITE);
}

void drawFace() {
  uint16_t cycleFrame = frameNumber % 180;
  bool blinking = (cycleFrame >= 150 && cycleFrame < 156) ||
                  (cycleFrame >= 160 && cycleFrame < 164);
  int8_t lookOffset = 0;

  if (cycleFrame < 45) {
    lookOffset = -6;
  } else if (cycleFrame < 90) {
    lookOffset = 0;
  } else if (cycleFrame < 135) {
    lookOffset = 6;
  }

  display.clearDisplay();
  drawEye(35, 35 + lookOffset, blinking);
  drawEye(93, 93 + lookOffset, blinking);
  display.display();
}

void setup() {
  Wire.begin();
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
    for (;;) {
    }
  }
  drawFace();
}

void loop() {
  unsigned long now = millis();
  if (now - lastFrameMs >= FRAME_INTERVAL_MS) {
    lastFrameMs = now;
    frameNumber++;
    drawFace();
  }
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik