Erratic Generator

Creative Coding and Design

About

Video Export from p5.js Sketch

Save mp4 videos from within a browser. No server needed.

The p5.js library itself does not have a way to export a video out of a sketch but because everything you do is simply drawing on an HTML5 Canvas, if you can find a library to export what’s on the Canvas, you should be able to use it. I recently stumbled upon a really good and simple library that exports to H.264 mp4 video straight out of a browser (no server needed) and made a simple example to make it work with a p5js sketch.

If you would rather check out the original library, here is the link, and I will also link to a few other resources at the end of this post.

https://github.com/TrevorSundberg/h264-mp4-encoder

I only spent a little time with the library, but by far, this has been the easiest and most reliable way to export a video out of a p5js sketch. I have tried a few other libraries that will export to either image sequences or .webm videos, but this library directly exports to .mp4 format, which I think is still better supported than .webm, and also supports the current version of p5js (v1.x)

The example included in the original repo explains everything for general use, but I made two quick examples if you just want to take it and use it in your p5js sketch.

Fixed-length Animation Loop

The first one is when you have a fixed-length animation. You can first define numFrames and play and record the animation. The only difference between a regular p5js sketch and this one is that I created the anim() function to replace draw() so I can loop it when I want to. You can just treat it like the draw function and do everything you need to inside. The UI is very minimal. If you only want to preview the animation, check the box, preview only. Here is the code:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/h264-mp4-encoder/embuild/dist/h264-mp4-encoder.web.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.1.9/lib/p5.js"></script>
<style>
html { font-family: sans-serif; font-size: 12px; }
</style>
</head>
<body>
<script>
/*
library used: https://github.com/TrevorSundberg/h264-mp4-encoder
a simple example exporting mp4 with p5js.
custom animation loop, not using draw(), fixed-length animation.
*/
let cwidth = 640
let cheight = 360
let button
let checkbox
const frate = 30 // frame rate
const numFrames = 100 // num of frames to record
let recording = true
function setup() {
createCanvas(cwidth, cheight)
frameRate(frate)
button = createButton('record')
button.mousePressed(record)
checkbox = createCheckbox(' preview only')
checkbox.style('display', 'inline')
checkbox.changed(()=> {
recording = !recording
})
anim(0) // display first frame
}
function anim(count) {
background(220)
textSize(128)
textAlign(CENTER, CENTER)
text(count, width / 2, height / 2)
}
function record() {
HME.createH264MP4Encoder().then(async encoder => {
encoder.outputFilename = 'test'
encoder.width = cwidth
encoder.height = cheight
encoder.frameRate = frate
encoder.kbps = 50000 // video quality
encoder.groupOfPictures = 10 // lower if you have fast actions.
encoder.initialize()
for (let i = 0; i < numFrames; i++) {
anim(i)
encoder.addFrameRgba(drawingContext.getImageData(0, 0, canvas.width, canvas.height).data)
await new Promise(resolve => window.requestAnimationFrame(resolve))
}
encoder.finalize()
if (recording) {
const uint8Array = encoder.FS.readFile(encoder.outputFilename);
const anchor = document.createElement('a')
anchor.href = URL.createObjectURL(new Blob([uint8Array], { type: 'video/mp4' }))
anchor.download = encoder.outputFilename
anchor.click()
}
encoder.delete()
})
}
</script>
</body>
</html>

Within the Regular Draw Loop

The second example is when you want to record a video while the animation is being played. It won’t loop but will just capture the frames as it plays.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/h264-mp4-encoder/embuild/dist/h264-mp4-encoder.web.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.1.9/lib/p5.js"></script>
</head>
<body>
<script>
/*
library used: https://github.com/TrevorSundberg/h264-mp4-encoder
a simple example exporting mp4 with p5js.
record video while animation is being played.
*/
let cwidth = 640
let cheight = 360
let button
let encoder
const frate = 30 // frame rate
const numFrames = 100 // num of frames to record
let recording = false
let recordedFrames = 0
let count = 0
// make sure encoder is ready before use
function preload() {
HME.createH264MP4Encoder().then(enc => {
encoder = enc
encoder.outputFilename = 'test'
encoder.width = cwidth
encoder.height = cheight
encoder.frameRate = frate
encoder.kbps = 50000 // video quality
encoder.groupOfPictures = 10 // lower if you have fast actions.
encoder.initialize()
})
}
function setup() {
createCanvas(cwidth, cheight)
frameRate(frate)
button = createButton('record')
button.mousePressed(() => recording = true)
}
function draw() {
background(220)
textSize(128)
textAlign(CENTER, CENTER)
text(count, width / 2, height / 2)
count++
// keep adding new frame
if (recording) {
console.log('recording')
encoder.addFrameRgba(drawingContext.getImageData(0, 0, encoder.width, encoder.height).data);
recordedFrames++
}
// finalize encoding and export as mp4
if (recordedFrames === numFrames) {
recording = false
recordedFrames = 0
console.log('recording stopped')
encoder.finalize()
const uint8Array = encoder.FS.readFile(encoder.outputFilename);
const anchor = document.createElement('a')
anchor.href = URL.createObjectURL(new Blob([uint8Array], { type: 'video/mp4' }))
anchor.download = encoder.outputFilename
anchor.click()
encoder.delete()
preload() // reinitialize encoder
}
}
</script>
</body>
</html>

It’s just amazing to think about how much a web browser can do these days. And thanks to people who generously share these libraries, I can have more fun with coding. Here are some resources that I found useful:

I publish the same articles on Medium, but they are behind the paywall. Your support will help me keep this website open to everyone. If you liked my contents, please consider supporting. Thank you!

Buy Me a Coffee at ko-fi.com
Copyright 2020-2021 • ErraticGenerator.com • Our Privacy Policy