kinetograph.
Guides

Audio

Play sound files with the Audio element, synthesise sound with onFrame, and measure the loudness of the mix.

A project has two sources of sound. Audio plays a file. onFrame schedules Web Audio synthesis at a frame. Both are mixed into one stereo track at 48 kHz.

Play a file

import { Audio, Sequence, staticFile } from 'remotion';

<Sequence from={30} durationInFrames={60}>
  <Audio src={staticFile('audio/line-1.wav')} volume={0.85} />
</Sequence>

src is a file path. Use staticFile for a file in public/. A clip starts at the frame where its Sequence starts, and it is silent inside a <Freeze>.

PropMeaning
volumeA number, or a function of the frame. The default is 1. The frame counts from the start of the enclosing Sequence.
trimBefore, trimAfterFrames of the file to skip at the start, and the frame of the file where it stops.
playbackRateThe speed.
loopPlay the file again from the start when it ends.
mutedSilence the clip.
toneFrequencyMultiplies the pitch, from 0.01 to 2. The speed does not change.

A render where every Audio, Video and OffthreadVideo is muted or at volume 0 and no onFrame callback runs has no audio stream in the file.

To raise the volume over the first second, pass volume={(frame) => interpolate(frame, [0, 30], [0, 1], { extrapolateRight: 'clamp' })}. Video plays the sound of its file too, and its volume, muted and toneFrequency props work the same way.

Synthesise sound

onFrame runs a function at a frame. The function gets a Web Audio context and the time to play at:

import { onFrame } from '@kinetographdev/audio';

onFrame(4, (ctx, t) => {
  const osc = ctx.createOscillator();
  const gain = ctx.createGain();
  osc.frequency.setValueAtTime(1800, t);
  osc.frequency.exponentialRampToValueAtTime(900, t + 0.05);
  gain.gain.setValueAtTime(0.25, t);
  gain.gain.exponentialRampToValueAtTime(0.001, t + 0.06);
  osc.connect(gain).connect(ctx.destination);
  osc.start(t);
  osc.stop(t + 0.07);
});
  • Call onFrame at the top level of the module, not inside a component. Each call adds one callback for one frame.
  • t is in seconds from the start of the rendered range. Schedule against t, not ctx.currentTime.
  • The context is an OfflineAudioContext from node-web-audio-api. It runs on the CPU.
  • For noise, use random from remotion with a named seed, such as random('whoosh-' + i). The same seed gives the same sound every render.

Mix and render

render mixes every Audio clip and every onFrame callback in the frames it draws. --range mixes only those frames.

The output codec follows the container:

  • .mp4 and .mov use AAC. .mov also takes PCM.
  • .webm uses Opus.
  • .gif has no sound.

Set the codec and bit rate with --audio-codec and --audio-bitrate. The default is 96 kbit/s per channel. See the CLI reference.

Check the loudness

npx kinetograph audio analyze examples/rajz-cards/index.tsx --scenes 0..60,60..125

The command mixes the audio and prints:

  • the integrated loudness in LUFS, measured to ITU-R BS.1770-4,
  • the true peak in dBTP,
  • the number of clipped samples,
  • the silences of 0.5 seconds or more below -60 dBFS.

--range limits the frames that are mixed. --scenes also measures each frame range on its own. Add --json for an object with the keys integratedLufs, truePeakDb, clippedSamples, silences and scenes.

@kinetographdev/audio also exports clearOnFrame, renderAudio and analyzeAudio. The signatures are in AUTHORING.md, section 8. For gaps, see What works.

On this page