15/02/2016 AndrewTosky Arduino, C++, LED

Fading LEDs with sine wave

Hi everybody,
the purpose of this post is to explain a simple technique concerning LED fading.
One common issue in fading a LED is blocking the execution of the loop() in a for loop as long as it performs the fading.

[sourcecode language="cpp" wraplines="false" collapse="false"]
void loop() {
  // fade in from min to max in increments of 5 points:
  for (int fadeValue = 0 ; fadeValue <= 255; fadeValue += 5) {
    // sets the value (range from 0 to 255):
    analogWrite(ledPin, fadeValue);
    // wait for 30 milliseconds to see the dimming effect

This approach wastes a lot of resources since the microcontroller can only perform this action until the execution goes on after the for loop.

One possibile solution is to drive LED fading with a sin/cos wave.
This means that at each iteration a harmonic oscillator value is assigned to the LED.

The simple harmonic oscillator equation is:
\(x(t)=A\cos(\omega t + \phi)\)

So, we basically need the amplitude A, ω the angular frequency \(\omega =\frac{2\pi}{T}\) and finally the optional φ which determines the starting point on the sine wave.

Let’s take a common cos wave shown below.

Cosine wave
Cosine wave

This function oscillates in range [-1;1]. To perform a complete LED fading we need to simply modify our cos wave to our purpose.
Arduino’s output is always between [0;255] (256 values); for this reason we need a cos wave that start at 128 and oscillates oscillates between 1 and 255 to get a reasonable range (128±127).
The picture below shows y=128+127cos(x), which is the starting point to create our harmonic oscillator.
The OFFSET 128 shifts my cos wave from 0 to 128 and the AMPLITUDE 127cos(..) tells me that we want to amplifier our wave by 127x.

Cosine wave - fading LED
Cosine wave – fading LED

What happens if we modify the OFFSET or the AMPLITUDE?
Let’s say we assign OFFSET = 150. This means that our wave OFFSET is shifted from 128 to 150 and it will oscillate in range [23;277] (note! same AMPLITUDE=127, oscillation 150±127) exceeding the upper bound and having 23 as min value. This means our led will never assume 0 value.
The upper and lower bound issues are easily fixed with a check function which clips the current value to 0 or 255 when it exceeds the bounds.

Modifying the AMPLITUDE, i.e. AMPLITUDE = 100, will cause a smaller oscillation around my OFFSET.
Play with these coefficients to get your best result!

Here there’s a sample code.

[sourcecode language="cpp" wraplines="false" collapse="false"] 

Andrea Toscano 2016

// Arduino LED PIN
#define LED_PIN 10
// Baudrate for Serial Communication
#define SERIAL_BAUD 115200

// Time period of fading in millisecs
#define PERIOD 2000
// Angular Frequency by definition
// No Phase
#define PHASE 0
// Offset of the sine wave
#define OFFSET 128
// Amplitude of the sine wave
#define AMPLITUDE 127

// Used to generate time for the cos wave
unsigned long timer = 0;

void setup() {
  // Uncomment for serial monitor


void loop() {
 timer = millis(); // updating time
 int ledValue = OFFSET + AMPLITUDE*(cos((OMEGA*timer)+PHASE));
 analogWrite(LED_PIN, checkValue(ledValue));

// Useful to avoid LED values outside the bounds [0;255]
int checkValue(int val) {
  if (val > 255)
    val = 255;
  else if(val < 0)
    val = 0;
  return val;

Comments (10)

  1. Results Point

    Jul 02, 2017 at 5:54 am

    Do you mind if I quote a few of your posts as long as I provide credit and sources back to your site?
    My blog is in the exact same area of interest as yours and my users would truly
    benefit from some of the information you provide here. Please let me know if this okay
    with you. Thank you!

    • AndrewTosky

      Jul 03, 2017 at 8:01 am

      Sure,no problem.
      Just provide credits and links to my website where needed, thank you!

  2. Mariano

    May 03, 2019 at 11:17 am

    Brilliant code!! How would you implement a “fade up – on for a period of time-fade down – off for a period” without delay()?

  3. Mariano

    May 03, 2019 at 12:33 pm

    What is the PHASE variable for?

  4. I love waves

    Apr 22, 2020 at 6:11 pm

    Thanks for this great code, but there is an error that prevents the phase variable from working properly. You are missing a set of parenthesis to include PHASE in the cosine calculation. The line should be:
    int ledValue = ledValue = OFFSET + AMPLITUDE*(cos((OMEGA*timer)+PHASE));

    • AndrewTosky

      May 05, 2020 at 11:04 am

      Thanks for pointing out 🙂

    • Mariano

      May 06, 2020 at 12:06 pm

      Can you please explain what is the error

    • Mariano

      May 06, 2020 at 12:25 pm

      What is the PHASE variable for or do? I have tried different values and cannot see the difference

  5. Pierre

    Mar 31, 2021 at 12:49 pm

    Thank you for posting this.
    At the bottom of your program, to keep the analogWrite between 0 and 255 you could use the constrain function like this:
    val = constrain(val, 0, 255);
    It is documented in the Arduino Reference under Math Functions.

    • AndrewTosky

      May 01, 2021 at 12:17 pm

      thanks for pointing it out

Post a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.