files/en-us/web/api/oscillatornode/index.md
{{APIRef("Web Audio API")}}
The OscillatorNode interface represents a periodic waveform, such as a sine wave. It is an {{domxref("AudioScheduledSourceNode")}} audio-processing module that causes a specified frequency of a given wave to be created—in effect, a constant tone.
{{InheritanceDiagram}}
<table class="properties"> <tbody> <tr> <th scope="row">Number of inputs</th> <td><code>0</code></td> </tr> <tr> <th scope="row">Number of outputs</th> <td><code>1</code></td> </tr> <tr> <th scope="row">Channel count mode</th> <td><code>max</code></td> </tr> <tr> <th scope="row">Channel count</th> <td><code>2</code> (not used in the default count mode)</td> </tr> <tr> <th scope="row">Channel interpretation</th> <td><code>speakers</code></td> </tr> </tbody> </table>OscillatorNode object, optionally providing an object specifying default values for the node's properties. As an alternative, you can use the {{domxref("BaseAudioContext.createOscillator()")}} factory method; see Creating an AudioNode.Also inherits properties from its parent, {{domxref("AudioScheduledSourceNode")}}.
AudioParam returned is read-only, the value it represents is not). The default value is 440 Hz (a standard middle-A note).AudioParam returned is read-only, the value it represents is not). The default value is 0.custom to use a {{domxref("PeriodicWave")}} to describe a custom waveform. Different waves will produce different tones. Standard values are "sine", "square", "sawtooth", "triangle" and "custom". The default is "sine".Also inherits methods from its parent, {{domxref("AudioScheduledSourceNode")}}.
type to custom.Also inherits events from its parent, {{domxref("AudioScheduledSourceNode")}}.
The following example shows basic usage of an {{domxref("AudioContext")}} to create an oscillator node and to start playing a tone on it. For an applied example, check out our Violent Theremin demo (see app.js for relevant code).
// create web audio api context
const audioCtx = new AudioContext();
// create Oscillator node
const oscillator = audioCtx.createOscillator();
oscillator.type = "square";
oscillator.frequency.setValueAtTime(440, audioCtx.currentTime); // value in hertz
oscillator.connect(audioCtx.destination);
oscillator.start();
The four built-in oscillator types are sine, square, triangle and sawtooth. They are the shape of the waveform generated by an oscillator. Fun fact: These are the defaults for most synths because they are waveforms which are easy to generate electronically. This example visualizes the waveforms for the different types at different frequencies.
<div class="controls">
<label for="type-select">
Oscillator type
<select id="type-select">
<option>sine</option>
<option>square</option>
<option>triangle</option>
<option>sawtooth</option>
</select>
</label>
<label for="freq-range">
Frequency
<input
type="range"
min="100"
max="800"
step="10"
value="250"
id="freq-range" />
</label>
<button data-playing="init" id="play-button">Play</button>
</div>
<canvas id="wave-graph"></canvas>
.controls {
display: flex;
gap: 1rem;
margin: 1rem 0;
align-items: center;
}
#wave-graph {
width: 500px;
height: 300px;
border: 4px solid var(--pink);
}
The code is in two parts: in the first part, we set up the sound stuff.
const typeSelect = document.getElementById("type-select");
const frequencyControl = document.getElementById("freq-range");
const playButton = document.getElementById("play-button");
const audioCtx = new AudioContext();
const osc = new OscillatorNode(audioCtx, {
type: typeSelect.value,
frequency: frequencyControl.valueAsNumber,
});
// Rather than creating a new oscillator for every start and stop
// which you would do in an audio application, we are just going
// to mute/un-mute for demo purposes - this means we need a gain node
const gain = new GainNode(audioCtx);
const analyser = new AnalyserNode(audioCtx, {
fftSize: 1024,
smoothingTimeConstant: 0.8,
});
osc.connect(gain).connect(analyser).connect(audioCtx.destination);
typeSelect.addEventListener("change", () => {
osc.type = typeSelect.value;
});
frequencyControl.addEventListener("input", () => {
osc.frequency.value = frequencyControl.valueAsNumber;
});
playButton.addEventListener("click", () => {
if (audioCtx.state === "suspended") {
audioCtx.resume();
}
if (playButton.dataset.playing === "init") {
osc.start(audioCtx.currentTime);
playButton.dataset.playing = "true";
playButton.innerText = "Pause";
} else if (playButton.dataset.playing === "false") {
gain.gain.linearRampToValueAtTime(1, audioCtx.currentTime + 0.2);
playButton.dataset.playing = "true";
playButton.innerText = "Pause";
} else if (playButton.dataset.playing === "true") {
gain.gain.linearRampToValueAtTime(0.0001, audioCtx.currentTime + 0.2);
playButton.dataset.playing = "false";
playButton.innerText = "Play";
}
});
As for the second part, we draw the waveform on a canvas using the {{domxref("AnalyserNode")}} we created above.
const dpr = window.devicePixelRatio;
const w = 500 * dpr;
const h = 300 * dpr;
const canvasEl = document.getElementById("wave-graph");
canvasEl.width = w;
canvasEl.height = h;
const canvasCtx = canvasEl.getContext("2d");
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
// draw an oscilloscope of the current oscillator
function draw() {
analyser.getByteTimeDomainData(dataArray);
canvasCtx.fillStyle = "white";
canvasCtx.fillRect(0, 0, w, h);
canvasCtx.lineWidth = 4.0;
canvasCtx.strokeStyle = "black";
canvasCtx.beginPath();
const sliceWidth = (w * 1.0) / bufferLength;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const v = dataArray[i] / 128.0;
const y = (v * h) / 2;
if (i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
x += sliceWidth;
}
canvasCtx.lineTo(w, h / 2);
canvasCtx.stroke();
requestAnimationFrame(draw);
}
draw();
[!WARNING] This example makes a noise!
{{EmbedLiveSample("Different oscillator node types", "", 500)}}
{{Specifications}}
{{Compat}}