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 delay(30); } [/sourcecode]
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.

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.

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"] /* FADING A LED WITH SINE WAVE 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 #define OMEGA 2*PI/PERIOD // 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 Serial.begin(SERIAL_BAUD); } 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; } [/sourcecode]
Comments (10)
Results Point
Jul 02, 2017 at 5:54 amDo 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 amHi!
Sure,no problem.
Just provide credits and links to my website where needed, thank you!
Mariano
May 03, 2019 at 11:17 amBrilliant code!! How would you implement a “fade up – on for a period of time-fade down – off for a period” without delay()?
Mariano
May 03, 2019 at 12:33 pmWhat is the PHASE variable for?
I love waves
Apr 22, 2020 at 6:11 pmThanks 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 amThanks for pointing out 🙂
Mariano
May 06, 2020 at 12:06 pmCan you please explain what is the error
Mariano
May 06, 2020 at 12:25 pmWhat is the PHASE variable for or do? I have tried different values and cannot see the difference
Pierre
Mar 31, 2021 at 12:49 pmThank 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 pmHi,
thanks for pointing it out