Back to Content

OscillatorNode

files/en-us/web/api/oscillatornode/index.md

latest7.9 KB
Original Source

{{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>

Constructor

  • {{domxref("OscillatorNode.OscillatorNode", "OscillatorNode()")}}
    • : Creates a new instance of an 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.

Instance properties

Also inherits properties from its parent, {{domxref("AudioScheduledSourceNode")}}.

  • {{domxref("OscillatorNode.frequency")}}
    • : An a-rate {{domxref("AudioParam")}} representing the frequency of oscillation in hertz (though the AudioParam returned is read-only, the value it represents is not). The default value is 440 Hz (a standard middle-A note).
  • {{domxref("OscillatorNode.detune")}}
    • : An a-rate {{domxref("AudioParam")}} representing detuning of oscillation in cents (though the AudioParam returned is read-only, the value it represents is not). The default value is 0.
  • {{domxref("OscillatorNode.type")}}
    • : A string which specifies the shape of waveform to play; this can be one of a number of standard values, or 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".

Instance methods

Also inherits methods from its parent, {{domxref("AudioScheduledSourceNode")}}.

  • {{domxref("OscillatorNode.setPeriodicWave()")}}
    • : Sets a {{domxref("PeriodicWave")}} which describes a periodic waveform to be used instead of one of the standard waveforms; calling this sets the type to custom.
  • {{domxref("AudioScheduledSourceNode.start()")}}
    • : Specifies the exact time to start playing the tone.
  • {{domxref("AudioScheduledSourceNode.stop()")}}
    • : Specifies the time to stop playing the tone.

Events

Also inherits events from its parent, {{domxref("AudioScheduledSourceNode")}}.

Examples

Using an OscillatorNode

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).

js
// 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();

Different oscillator node types

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.

html
<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>
css
.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.

js
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.

js
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

{{Specifications}}

Browser compatibility

{{Compat}}

See also