Source: https://mokxi.com/learn/arduino-pwm-fade
Updated: 2026-09-27

Learn

# Fading an LED with analogWrite()

Written by the Mokxi team, updated September 27, 2026

Sign up free or see every lesson

- 192 parts on the bench
- 25 boards running now
- 1.00x real time, on every board

Click to open it in the editor

Real hardware PWM on timer 1, breathing an LED on pin 9 at 490 Hz.

A digital output pin has exactly two states, on or off; it cannot sit at 2.5 volts to make an LED glow at half brightness. Pulse-width modulation gets the same effect a different way: switch the pin on and off fast enough, and vary how much of each cycle it spends on, and anything downstream that cannot react as fast as the switching (an LED, a motor, your own eye) only ever sees the average, which behaves like a real in-between voltage.

The circuit above is the Arduino Uno's built-in `fade` program, running for real, not a canned animation standing in for one. `analogWrite()` on pin 9 puts the ATmega328P's timer 1 into 8-bit phase-correct PWM with a 64 prescaler, which works out to 16 MHz divided by 64 times 510, about 490 Hz. That is not a rounded guess; it is what the timer hardware actually produces at that prescaler and mode, and once it is set the timer holds the duty cycle entirely on its own between updates.

Download the lesson plan (PDF)

## Which pins support analogWrite

Only six pins on the Uno can do this: 3, 5, 6, 9, 10 and 11, marked with a tilde on the board's own silkscreen. Those are the pins with a hardware timer's compare output wired to them; call `analogWrite()` on any other pin and the Arduino core has no timer behind it to ask, so nothing happens the way this page describes. Three separate timers cover the six pins between them (timer 0 for 5 and 6, timer 1 for 9 and 10, timer 2 for 3 and 11), which is also why using `analogWrite()` on one of a pair can shift the frequency `delay()` and `millis()` rely on if the wrong timer is repurposed, worth knowing, rarely worth worrying about in an ordinary sketch.

## The code

The sketch below only ever changes one thing, `analogWrite(LED_PIN, breath[step])`, thirty times a second, stepping through a 64-entry brightness table. The timer does the actual switching between updates without any further help from the CPU, which is the real advantage of hardware PWM over toggling a pin in software: once it is configured, the waveform never glitches even if the sketch is briefly busy doing something else.

firmware/examples/fade (the exact program running above)

## Why 8 bits

`analogWrite()` takes a value from 0 to 255, eight bits, on every pin it supports, even the ones sitting on timer 1, which is itself a 16-bit timer capable of a much finer duty cycle if configured by hand. The Arduino core sticks to 8 bits because that is the resolution the same function needs to behave identically on every supported pin, including the ones on 8-bit timers that have no finer setting available at all; matching the widest common resolution rather than the narrowest would mean the same call producing a different actual brightness step size depending which pin it was called on.

256 steps is also, in practice, finer than the eye needs at the low end of a breathing pattern like this one, and coarser than it can tell apart in the middle: which is exactly why the table above is not a straight ramp.

## The brightness curve

Human brightness perception is closer to logarithmic than linear: doubling the actual light output does not look twice as bright, it looks like a modest step up, while the same absolute change near the dark end looks dramatic. A duty cycle that ramps in a straight line from 0 to 255 therefore looks like it rushes through the bright half and crawls through the dark half. The table in the sketch above is one cycle of (1 − cosine) divided by 2, which spends more of its steps near the dark end and fewer near the bright end, and the result reads as a smooth, even breath rather than a ramp with an obvious kink in it.

What you see on the LED is also not the instantaneous switching. Mokxi's LED probe reports a short, perceptually weighted average of its own brightness with a time constant close to 8 milliseconds, in the same range the eye's own response takes to settle; at 490 Hz each PWM cycle is about 2 milliseconds, comfortably faster than that average can follow, so the reported brightness tracks the duty cycle to within a couple of percent instead of flickering once per cycle. That is the same averaging a retina does, computed the same way, not a rendering shortcut standing in for it.

## PWM beyond LEDs

Everything above generalizes past a single LED. A small DC motor driven through PWM spins at a speed roughly proportional to the duty cycle, for the same reason an LED dims proportionally: the motor cannot react fast enough to see individual pulses, only their average. A hobby servo, by contrast, is not actually being PWM-dimmed at all in the usual sense; it reads the width of each pulse directly, at a fixed roughly 50 Hz repetition rate, to decide an angle rather than a brightness, which is a different use of the same idea and worth not confusing with `analogWrite()` on an LED.

A speaker or a small piezo buzzer is the third common case, and it sits between the two: rather than reading an average, it responds to the switching itself as a tone, at whatever frequency the PWM runs at, which is why the `tone()` function exists as a separate call from `analogWrite()` even though both ultimately just switch a pin on a schedule. `analogWrite()`'s fixed 490 Hz on most Uno pins would give a flat, unchanging pitch; `tone()` varies the frequency itself rather than the duty cycle, which is the right tool when the goal is a note rather than a brightness or a speed.

## Questions

Can I get finer than 8-bit PWM on the Uno?

Yes, by configuring a 16-bit timer's registers directly instead of calling analogWrite(), which trades the Arduino core's cross-pin consistency for the extra resolution timer 1 can actually provide.

Does the PWM frequency matter for visible flicker?

Only if it drops low enough that a cycle takes longer than the eye's averaging window, roughly 8 to 16 milliseconds. At 490 Hz, each cycle is about 2 milliseconds, well clear of that, which is why hardware PWM on the Uno never looks like it is strobing.

Why does the fade look different on an ESP32-C3?

It need not. The board ships both: the fade example bit-bangs the waveform in software, and pwmfade hands it to LEDC, the chip's PWM controller, which any GPIO can carry rather than only six fixed pins. Comparing the two on the ESP32-C3 board page is the point of shipping both.

Can I use analogWrite on any digital pin?

No. Only pins 3, 5, 6, 9, 10 and 11 on the Uno have a timer's compare output wired to them; calling it on any other pin does nothing.

Does a higher PWM frequency mean a brighter LED?

No. Frequency and duty cycle are independent: the frequency only has to be fast enough that whatever is watching (an eye, a motor) cannot follow individual pulses. Brightness comes entirely from the duty cycle, the fraction of each cycle spent on.

Related

## Keep going

Picking a Resistor for an LED The 555 Astable: Timed by a Capacitor, Not a Crystal PWM to an Analog Voltage With an RC Filter Raspberry Pi Pico PWM: Slices, Channels and 1 kHz Arduino LED Not Lighting Up or Dim: What to Check The Arduino Uno simulator The ESP32-C3 simulator Timing a sketch without delay() The LED, on its own part page

## Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.

Start building Open the editor
