Using Sigmoid Functions to Create Data “Envelopes”

I had the challenge of plotting a sine wave of arbitrary frequency, but the beginning and ends of the plot had to fade in/out so that it was at 0 on both ends.

My first attempt was using a piecewise function:

  1. 0 <= x <= 0.2 : Sine wave segment rising from 0 to 1.
  2. 0.2 < x < 0.8 : Constant value of 1.
  3. 0.8 <= x <= 1 : Sine wave segment falling from 1 to 0.

However, this approach always produced a visible “seam” at the transition points between functions. I wanted a solution where there was no visual cue where the fade in/out ranges began.

I turned to Sigmoid functions, as they had the smooth curve I was looking for, but didn’t have to be clipped to prevent them from oscillating in a direction I didn’t want. However, a single Sigmoid function will only do a single traversal from 0 to 1 or 1 to 0 (or other values based on how you scale it). I realized since the functions approach 1, they could be multiplied together to produce an envelope shape that rose smoothly from 0 to 1 at the start, stayed at 1 through the middle, and fell smoothly from 1 to 0 at the end. I applied various tweaks to equation to change the slope of the functions and position them within the 0…1 range (with everything outside that range being a 0).

This is the result.

A break-down of the key equation components:

  • The -60 term controls the slope of the curve. Higher values produce a steeper curve. In my case, I wanted a curve that finished with a width of about 0.2. There is no requirement that both of the Sigmoid functions have the same slope value, so if you wanted a faster rise and a slower fall, that’s fine.
  • The (x - 0.1) term shifted the leading edge of the “fade in” Sigmoid right so that it crosses the y-midpoint (0.5) at the x = 0.1.
  • The “fade out” Sigmoid flips the function by using (0.9 - x) as the multiplier. This ensures that the function crosses the y-midpoint at x = 0.9.
  • There is nothing magical about the use of the natural log (e) number. You can use any base, with lower values producing more gradual curves and higher values producing steeper ones. This is just how the sigmoid function is defined, so I went with it.

Here’s the full function in Swift:

(1 / (1 + pow(2.718281828459, -60 * (x - 0.1)))) * (1 / (1 + pow(2.718281828459, -60 * (0.9 - x))))