DEV Community

Flower Rain Studio
Flower Rain Studio

Posted on

Building a browser-local photo slideshow exporter with Canvas and MediaRecorder

Turning a set of photos into a video does not require a server upload pipeline. A browser can draw frames to a canvas, combine that canvas stream with audio, and record the result locally with MediaRecorder.

This is the approach behind Photo2Reel, a small free slideshow maker. The useful constraint was deliberate: selected photos and audio must stay in the browser, with no account, remote rendering queue, or watermark.

The rendering loop

Each photo is decoded in the browser and drawn into a 1280 × 720 canvas. The renderer calculates the active slide from elapsed time rather than scheduling a separate timer for every image. That keeps the preview and exported sequence on the same timeline.

const stream = canvas.captureStream(30);
const recorder = new MediaRecorder(stream, { mimeType });

function drawFrame(elapsedMs) {
  const slide = findSlide(elapsedMs, durations);
  drawCover(canvas, slide.image);
  requestAnimationFrame(drawFrame);
}
Enter fullscreen mode Exit fullscreen mode

The important detail is cover drawing. Landscape photos can fill the frame. Portrait photos should be placed against a background rather than stretched; otherwise a local tool produces the same distorted output people dislike in quick slideshow apps.

Adding music without sending it anywhere

For uploaded audio, an AudioContext connects the media element to a MediaStreamAudioDestinationNode. Its audio track is added to the canvas video track before recording. For short built-in tones, the same destination can receive generated oscillator audio instead.

The exported blob is created only after MediaRecorder emits its final data. Object URLs provide the download link and are revoked when a new render begins, which avoids retaining large files through repeated previews.

Export formats are a capability check

There is no single browser-safe video MIME type. The recorder first checks candidates with MediaRecorder.isTypeSupported(), typically preferring WebM codecs and falling back to MP4 only when the browser exposes it. The download extension must match the selected MIME type.

const mimeType = [
  'video/webm;codecs=vp9,opus',
  'video/webm;codecs=vp8,opus',
  'video/webm',
  'video/mp4'
].find(MediaRecorder.isTypeSupported);
Enter fullscreen mode Exit fullscreen mode

This also means the browser does the computational work. Big images or long sequences should be tested in smaller batches first, especially on mobile devices.

The complete front-end source is available on GitHub. The live tool is useful for checking the result on an actual device, but the central idea is portable: Canvas plus MediaRecorder is enough for a private, browser-local photo-to-video workflow.

Top comments (0)