Waveform display is not supported by your browser. Spectrum display is not supported by your browser.
```// Generate samples based on the "time" parameter.
var sample = sin(220.0 * 2*PI*time);
// Store final sample values in the "left" and "right" variables.
left = sample;
right = sample;```
50

## Help

Use the editor to write Javascript code to generate an audio signal in real time.

The following built-in variables are available:

• time [input] -- elapsed time in seconds
• left [output] -- store the sample value for the left channel here. Range is [-1.0...1.0].
• right [output] -- store the sample value for the right channel here. Range is [-1.0...1.0].
For the sake of convenience, the following Math functions are defined at the global scope (no "Math." prefix required: PI, abs(x), sign(x), sqrt(x), cbrt(x), min(x,y), max(x,y), clamp(x,minX,maxX), ceil(x), floor(x), trunc(x), fract(x), log(x), log10(x), exp(x), pow(x,y), sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), atan2(y,x)

## Tutorial:

Basic Wave Generation
Let's start with the simplest example possible: a sine wave:

var sample = sin(time); left = sample; right = sample;

The resulting wave undergoes a single cycle once every 2*PI (=6.28...) seconds. This gives a frequency of 1/6.28 = 0.159 Hz, which is well beyond the limits of human hearing (roughly 20Hz through 20,000 Hz). We'll need a much higher frequency if we want to hear anything. First, let's generate a 1Hz wave, by multiplying the time by 2*PI:

var sample = sin(2*PI*time); left = sample; right = sample;

Next, we declare a freq variable to store the desired wave frequency. We'll start at 1.0 for now:

var freqHz = 1.0; var sample = sin(freqHz * 2*PI*time); left = sample; right = sample;

This is still inaudible, but if we increase the value of freqHz, we should eventually start to hear a low bass hum at around 20Hz. Try it! For reference, the pitch most orchestras tune to (A above middle C) is 440Hz.

var freqHz = 20.0; var sample = sin(freqHz * 2*PI*time); left = sample; right = sample;

We can add a decay to the sound by scaling the sample values by exp(-x). At x=0, exp(0) returns 1.0; as x gets larger (and -x gets closer to -infinity), exp(-x) approaches zero. By scaling the input to exp(), we get a longer or shorter decay.

var freqHz = 440.0; // higher value -> higher pitch var decaySpeed = 4.0; // higher value -> faster decay (shorter notes) var sample = sin(freqHz * 2*PI*time) * exp(-decaySpeed*time); left = sample; right = sample;

Now let's add some rhythm to the sound. Instead of passing time directory to exp(), we use fract() to only pass the fractional part of time. This forces the decay factor to reset to 1.0 every second. We can go one step further and define a scale factor for the fract() term so that we can express the tempo in terms of a more familiar unit like beats per minute (bpm).

var freqHz = 440.0; // higher value -> higher pitch var decaySpeed = 4.0; // higher value -> faster decay (shorter notes) var tempoBpm = 60; // higher value -> faster rhythm var sample = sin(freqHz * 2*PI*time) * exp(-decaySpeed*fract(tempoBpm/60 * time)); left = sample; right = sample;

Sine waves are boring. Let's generate a more interesting waveform, like a square wave. First let's pull the wave generator into its own function.

var freqHz = 440.0; // higher value -> higher pitch var decaySpeed = 4.0; // higher value -> faster decay (shorter notes) var tempoBpm = 60; // higher value -> faster rhythm function waveSine(freq,t) { return sin(freq * 2*PI*t); } var sample = waveSine(freqHz,time) * exp(-decaySpeed*fract(tempoBpm/60 * time)); left = sample; right = sample;

We can build a square wave a few different ways. The first uses Fourier analysis to break a square wave into an infinite series of sine waves (for more details, see the Wikipedia entry for square waves). The more terms we add, the more closely we approximate a true square wave. Try slowly increasing the value of numHarmonics below, or jump straight to around 50 for a pretty good approximation of a square wave.

var freqHz = 440.0; // higher value -> higher pitch var decaySpeed = 4.0; // higher value -> faster decay (shorter notes) var tempoBpm = 60; // higher value -> faster rhythm function waveSine(freq,t) { return sin(freq * 2*PI*t); } function waveSquare(freq,t) { var sample=0; var numHarmonics=1; // higher values -> better approximation var iHarm; for(iHarm=0; iHarm<numHarmonics; iHarm += 1) { sample += sin(freq * (2*iHarm+1)*2*PI*t) / (2*iHarm+1); } sample *= 4/PI; return sample; } var sample = waveSquare(freqHz,time) * exp(-decaySpeed*fract(tempoBpm/60 * time)); left = sample; right = sample;

...or we could just take the high road, and observe that it's quicker and easier to generate a perfect square wave by simply taking the sign (+1 or -1) of a regular sine wave. But the Fourier approach is still worth knowing about!

var freqHz = 440.0; // higher value -> higher pitch var decaySpeed = 4.0; // higher value -> faster decay (shorter notes) var tempoBpm = 60; // higher value -> faster rhythm function waveSine(freq,t) { return sin(freq * 2*PI*t); } function waveSquare(freq,t) { return sign( sin(freq * 2*PI*t) ); } var sample = waveSquare(freqHz,time) * exp(-decaySpeed*fract(tempoBpm/60 * time)); left = sample; right = sample;

Phase Modulation
Another way we can change the timbre of our waveform is to use a technique called phase modulation. We start by adding a second sine wave as an offset to the parameter of the main sine wave, with a scale factor called the "modulation index". Try increasing the modulation index to hear the difference in the resulting tone.

var freqHz = 440.0; // higher value -> higher pitch var decaySpeed = 4.0; // higher value -> faster decay (shorter notes) var tempoBpm = 60; // higher value -> faster rhythm var modulationIndex = 1.0; var sample = sin(freqHz * 2*PI*time + modulationIndex*sin(freqHz * 2*PI*time)) * exp(-decaySpeed*fract(tempoBpm/60 * time)); left = sample; right = sample;

...and once you have a modulation index, the obvious next step is to modulate it (with yet ANOTHER sine wave). As usual, try playing with the constants to see what effect they have on the output.

var freqHz = 220.0; // higher value -> higher pitch var decaySpeed = 4.0; // higher value -> faster decay (shorter notes) var tempoBpm = 60; // higher value -> faster rhythm var modulationIndex = 10.0 + 15.0*sin(3.0*time); var sample = sin(freqHz * 2*PI*time + modulationIndex*sin(freqHz * 2*PI*time)) * exp(-decaySpeed*fract(tempoBpm/60 * time)); left = sample; right = sample;

## Examples:

var modulationIndex = 10*sin(time); var freqHz = 220; var drumDistortion = 1000.0; var phaseOffset = modulationIndex * sin(freqHz * 2*PI*time); var sample = sin(freqHz * 2*PI*time + phaseOffset) * exp(-4.0*fract(4*time)); var kick = clamp( drumDistortion*sin( 100*exp( -10*fract(2.0*time ))), -1, 1); var hat = clamp( drumDistortion*sin( 1000*exp(-100*fract(2.0*time + 0.5))), -1, 1); sample += kick; sample += hat; left = sample; right = sample;

## TODO:

• UI controls for:
• Audio buffer size
• Save current program to LocalStorage (with name)
• Load program from LocalStorage (by name)
• Delete program from LocalStorage (by name)
• Move keyboard shortcuts from editor to window, so they're active without editor focus.
• Move editor and wave display into a frame?
• Error reporting (syntax error, failure to set left/right, etc.)