Skip to content

Render Visuals to Video

render(screen, n) renders what is routed to screen for n cycles. On macOS it writes screen.mp4: 60 fps H.264 video with the master mix as AAC audio, using the native hardware encoder. Add master or a track to the targets to write their WAV files too. On other platforms it writes a PNG frame sequence.

Route the picture to screen, then render master and screen together:

use "std/instruments" { Sampler, Kit };
use "std/visuals" as gfx;
set_project_title("Frames");
let drums = AudioTrack("drums");
drums.set_instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
let field = gfx.Noise("field");
let soft = gfx.Blur("soft");
soft.parameter("size") << drums.rms.smooth(80).range(1, 40);
field >> soft >> screen;
render(#[master, screen], 1);

Then, in the project folder:

Terminal window
resonon run

A level or spectrum bound to the picture reads the render’s own audio, so the frames move with the mix. No window has to be open.

render(screen, 1);

The master mix is included in the macOS MP4. No separate master.wav is written. On other platforms, the mix drives the bound levels but is not saved.

On macOS, the movie is in the render’s folder under renders/:

renders/
frames_2026-09-30_18-00-00/
master.wav only when master is a target
screen.mp4 picture and master mix

There are 60 frames a second, at the composition size (1920 × 1080 by default). Set it with screen.resolution(width, height) before rendering. H.264 uses 4:2:0 colour with BT.709 tags; both dimensions must be even. An odd dimension errors before audio is bounced or files are written. No PNG frames or external encoder are needed on macOS.

Other platforms write frames/frame_00000.png onwards. To assemble these into a video with audio, include master in the targets and run an encoder such as ffmpeg in the render folder:

Terminal window
ffmpeg -framerate 60 -i frames/frame_%05d.png -i master.wav -c:v libx264 -pix_fmt yuv420p -c:a aac video.mp4

Call song.play() to put the arranged screen lane on screen, then render it for the song’s length:

let verse = section(1);
verse[drums] << [bd sd bd sd];
verse[screen] << field;
let chorus = verse.clone();
chorus[screen] << soft;
let song = arrange(#[verse, chorus]);
song.play();
render(#[master, screen], song.length());

The picture changes on the first frame at each span’s start. Gaps and frames past the lane’s end are black. Feedback trails keep their state across cuts and hold still during gaps, as in the live window.