Skip to main content

· 6 min read
Jonny Burger

Welcome to the release notes of Remotion 1.4! This is a big release adding support for more use cases and makes Remotion more stable, intuitive and robust.

Support for HEVC and WebM codecs

Instead of just supporting H.264, you can now also encode a video in H.265 (HEVC), or as a WebM (in either VP8 or VP9 codec). These codecs will result in smaller file sizes, but have some trade-offs. To help you decide which codec to use, there is now an encoding guide in the docs. See also the documentation for changing the codec in the config file as well as in the CLI.

Transparent videos

Did you know that Chrome and Firefox support video with alpha channels? Play the video and click the button below to dynamically change the background.

Remotion now has enough configurability to enable you to render transparent videos. There is a new documentation page for it, which also gives you guidance on how to render a fallback video for unsupported browsers.

New random() API

Let's say you want to render 100 random particles in a video. A common mistake is to generate random values and store them in a state. The following is an anti-pattern in Remotion:

tsx
export const MyComp = () => {
// ⚠️ Bug! Random values will change during render
const [particles] = useState(() => {
return new Array(100).fill(true).map(() => ({
x: Math.random(),
y: Math.random(),
}));
});
};
tsx
export const MyComp = () => {
// ⚠️ Bug! Random values will change during render
const [particles] = useState(() => {
return new Array(100).fill(true).map(() => ({
x: Math.random(),
y: Math.random(),
}));
});
};

While this will work while previewing the video, during rendering, the random numbers will change during frames. This is because Remotion spins up multiple instances of Chrome and the random numbers will be different in each instance.

To help you avoid this mistake, there is now a new ESLint rule that will warn when you use Math.random(). Instead using it, you can use the new random() API. It will take a seed parameter and output a number between 0 and 1. The point of it is: As long as you pass the same seed, you get the same output! That way you don't get unintended effects while multithreaded rendering is performed.

tsx
import { useState } from "react";
import { random } from "remotion";
 
export const MyComp = () => {
// ✅ Pseudo-random values that will be same across threads
const [particles] = useState(() => {
return new Array(100).fill(true).map((_, idx) => ({
x: random(`x-${idx}`),
y: random(`y-${idx}`),
}));
});
};
tsx
import { useState } from "react";
import { random } from "remotion";
 
export const MyComp = () => {
// ✅ Pseudo-random values that will be same across threads
const [particles] = useState(() => {
return new Array(100).fill(true).map((_, idx) => ({
x: random(`x-${idx}`),
y: random(`y-${idx}`),
}));
});
};

Want to learn more? Read the new documentation page about using randomness.

More configuration options

Several new configuration options have been added as CLI flags, config file entries and to the SSR API:

  • setCrf() controls the tradeoff between quality and file size of the output file.
  • setQuality() allows you to control the JPEG quality of the frames rendered.
  • setCodec() allows you to select between 4 different codecs (as mentioned above).
  • setImageSequence() allows you to skip the stitching process and output only an image sequence
  • setImageFormat() allows you to explicitly select either PNG or JPEG as the format for the rendered frames.
  • setPixelFormat() has a new accepted value, yuva420p, which is necessary for transparent videos.

The addition of setImageSequence() and setImageFormat() as well as setCodec() makes the previous configuration options setOutputFormat() and --png obsolete. and they are therefore now deprecated. While they still work, we encourage you to use the new configuration options which are much more granular - for example now you can render a JPEG sequence if you wish to.

Keyboard controls in the editor

Thanks to an awesome pull request by Arthur Denner, keyboard navigation in the editor is much improved! Reading the PR is highly recommended because it's insightful and makes you realize that it's not hard to make a website keyboard-navigateable.

keyboard-support.gif

Progress bar for stitching frames together

Don't be surprised if the newly added codecs are much slower than the default H.264. It's normal. But at least now you can always check the progress of the rendering!

stitching-progress.png

New layout prop for <Sequence>

Few people have rightly pointed out that the <Sequence> component will absolutely position it's children and there is no way to opt out of it. While we cannot change this now because of backwards-compatibility, you can now pass layout="none" as a prop to opt out of any layout influence that <Sequence> has.

Asynchronously determining composition metadata

There's a small breaking change in this release - delayRender works during the 'evaluation' phase. This is the phase where Remotion analyses your project and determines all compositions and does some validation. Watch out for delayRender() calls outside components, as they might block the evaluation phase now since no components are being rendered during that phase.

The benefit is that now your compositions can take in data that has been asynchronously fetched. For example the following is now possible:

tsx
export const RemotionVideo: React.FC = () => {
const [videoLength, setVideoLength] = useState(null)
useEffect(() => {
const handle = delayRender();
determineVideoLength()
.then((duration) => {
setVideoLength(duration)
continueRender(handle)
})
.catch(err => /**/)
}, [])
if (videoLength === null) {
return null;
}
return (
<>
<Composition
id="HelloWorld"
component={HelloWorld}
durationInFrames={videoLength}
fps={30}
width={1920}
height={1080}
/>
</>
)
}
tsx
export const RemotionVideo: React.FC = () => {
const [videoLength, setVideoLength] = useState(null)
useEffect(() => {
const handle = delayRender();
determineVideoLength()
.then((duration) => {
setVideoLength(duration)
continueRender(handle)
})
.catch(err => /**/)
}, [])
if (videoLength === null) {
return null;
}
return (
<>
<Composition
id="HelloWorld"
component={HelloWorld}
durationInFrames={videoLength}
fps={30}
width={1920}
height={1080}
/>
</>
)
}

Stay tuned for a new API that will allow you to measure video and audio duration!

End to End tests for Windows, macOS and Ubuntu

While Remotion is primarily developed on macOS, other operating systems shouldn't have a sub-par experience or get bugs. While it does not replace manual testing, it helps that there are now some end-to-end tests for rendering videos and the test suite now runs on all 3 operating systems. Going forward, I will add more tests and contributors are also encouraged to do so.

ESLint rule for wrong asset import

In Remotion, you shouldn't specify the src as filepath string, but import the asset instead. To help you not make this mistake, there's a new ESLint rule:

tsx
import {Img} from 'remotion';
import hi from './hi.png';
// ✅ Correct: Using an import statement
<Img src={hi} />
// ⚠️ Warning since 1.4: Import the asset instead
<Img src="./hi.png"/>
tsx
import {Img} from 'remotion';
import hi from './hi.png';
// ✅ Correct: Using an import statement
<Img src={hi} />
// ⚠️ Warning since 1.4: Import the asset instead
<Img src="./hi.png"/>

Paying top contributors

Since Remotion will make money by selling licenses to bigger companies, it's only fair to give contributors their share as well! I have made offers to two top contributors so far to compensate them for their time spent so far and for potential future work. Gladly, they both accepted!

Various improvements

Up next...

Some awesome pull requests came in, the most notable one adds audio support! We are also working on allowing you to customize Puppeteer parameters and making the installation process much faster by not downloading a copy of Chromium. Keep your eyes open for more major improvements coming soon.

· 4 min read
Jonny Burger

Only 1 week after the initial launch, here is Remotion 1.3! In just 7 days, we have merged over 40 pull requests - just amazing! Let's go over the changes in this release.

2x faster - again!

2 days ago, the rendering time was cut in half, and in this release, we managed to half it again! Check out this benchmark of the Spotify Wrapped example:

Remotion 1.1

hyperfine --min-runs 5 'npm run build -- --overwrite --concurrency=16' Benchmark #1: npm run build -- --overwrite --concurrency=16 Time (mean ± σ): 98.972 s ± 0.650 s [User: 123.329 s, System: 10.103 s] Range (min … max): 97.951 s … 99.540 s 5 runs
hyperfine --min-runs 5 'npm run build -- --overwrite --concurrency=16' Benchmark #1: npm run build -- --overwrite --concurrency=16 Time (mean ± σ): 98.972 s ± 0.650 s [User: 123.329 s, System: 10.103 s] Range (min … max): 97.951 s … 99.540 s 5 runs

Remotion 1.3

hyperfine --min-runs 5 'npm run build -- --overwrite --concurrency=16' Benchmark #1: npm run build -- --overwrite --concurrency=16 Time (mean ± σ): 17.921 s ± 0.224 s [User: 36.492 s, System: 3.482 s] Range (min … max): 17.650 s … 18.264 s 5 runs
hyperfine --min-runs 5 'npm run build -- --overwrite --concurrency=16' Benchmark #1: npm run build -- --overwrite --concurrency=16 Time (mean ± σ): 17.921 s ± 0.224 s [User: 36.492 s, System: 3.482 s] Range (min … max): 17.650 s … 18.264 s 5 runs

From 98 to 18 seconds - that's 5.5 times faster! At the same time, we have reached an important milestone: This 19-second long 720p video was rendered faster than realtime. Granted, my computer is faster than most (8-core Intel i9-9900K chip), but still very impressive!

We achieved this performance gain through various Puppeteer rendering pipeline optimizations. Big shoutout to jeetiss who implemented a sophisticated performance optimization that doesn't require a page reload for each frame anymore.

Plain-Javascript support

My goal was to force Typescript on everybody - but I failed. Support for plain Javascript is now added! See here how to enable it. Proceed carefully 🙈

Type-safe configuration file

Many options which you could pass in via CLI flags, you can now also add by adding a remotion.config.ts file in the repo. For example, if you want to increase the parallelism to the amount of threads you have and never want to write --overwrite, you can add the following to the config file:

tsx
import os from "os";
import { Config } from "remotion";
Config.Rendering.setConcurrency(os.cpus().length);
Config.Output.setOverwriteOutput(true);
tsx
import os from "os";
import { Config } from "remotion";
Config.Rendering.setConcurrency(os.cpus().length);
Config.Output.setOverwriteOutput(true);

You can see all the options on this page. The goal of making a config file in Typescript is to provide autocomplete, to easily highlight deprecated options and making it easier to show how to migrate in case the options change in the future.

Easing API

While there was an Easing API, it was undocumented. Learn which Easing methods are available and how to use it with interpolate()!

tsx
import { interpolate, Easing } from "remotion";
interpolate(frame, [0, 100], {
easing: Easing.bezier(0.8, 0.22, 0.96, 0.65),
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
tsx
import { interpolate, Easing } from "remotion";
interpolate(frame, [0, 100], {
easing: Easing.bezier(0.8, 0.22, 0.96, 0.65),
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});

The small things

In case you missed it

Another example was added - Spotify Wrapped! This is a fully dynamic example where you can replace all data with a command line flag. There's a 2 hour tutorial on YouTube and the source code is on GitHub.

· 2 min read
Jonny Burger

You have tested Remotion and provided an enormous amount of feedback! Thanks to it, this version of Remotion is vastly improved 🎉

Rendering time cut in half!

It turns out rendering each frame as JPEG is much faster than rendering them in PNG and results in no visible quality difference. Using this trick, the time of the 'Rendering Frames...' of an example video went down from 14 seconds to 6.5 seconds! That's more than twice as fast.

Now that the rendering process is JPEG-based, a new flag --quality was added to command line. For server-side rendering, you may pass in a new imageFormat option. If you render a PNG sequence, Remotion will of course still deliver PNGs.

The goal is to make rendering even faster, finding further optimizations is going to be a very interesting task!

Windows support

Windows support is very important (just like PHP). All the bugs that prevented proper Windows installation have now been fixed. Plus I now have a proper Windows setup so from now on Remotion will be tested on Windows as well!

Discord community

We now have a Discord! Join now and chat about Remotion!

Licensing now available

The terms of the company license have now been worked out and a billing system with Stripe has been set up. Contact me for pricing!

Roadmap

A GitHub Project board has been setup with the goal of indicating the next priorities. Check it out!

Miscellaneous

· 2 min read
Jonny Burger

What an extraordinary launch! In less than 24 hours, the Remotion announcement video has gotten more than 100'000 views on Twitter. The feedback was absolutely overwhelming!

Now it's time to iterate and make Remotion better. This release contains two new features: Customizing the Webpack configuration and new <Img> and </IFrame> components!

Customizing Webpack configuration

See docs

People had wild ideas on what to do with Remotion like importing MDX files or using React Native components. Now it is possible!

When providing the possibility on how to configure Remotion, it was of big importance to provide both a high degree of flexibility and a good developer experience. This is how you update the webpack config:

remotion.config.ts

ts
import { overrideWebpackConfig } from "@remotion/bundler";
overrideWebpackConfig((currentConfiguration) => {
return {
...currentConfiguration,
module: {
...currentConfiguration.module,
rules: [
...currentConfiguration.module.rules,
// Add more loaders here
],
},
};
});
ts
import { overrideWebpackConfig } from "@remotion/bundler";
overrideWebpackConfig((currentConfiguration) => {
return {
...currentConfiguration,
module: {
...currentConfiguration.module,
rules: [
...currentConfiguration.module.rules,
// Add more loaders here
],
},
};
});

There is a new Remotion config file, that you can write in Typescript. Updating the Webpack config uses the reducer pattern: You get the default configuration and it is your responsibility to return an updated Webpack config. You get type checking, auto completion and the flexibility to either only update a single property or pass in a completely new configuration. No black magic here - since this is just a pure function, the outcome will be very predictable.

New <Img /> and <IFrame /> components

See docs

The purpose of these new components is to help with something that is easy to overlook in Remotion: Images or Iframes are being rendered, but Remotion does not wait until the loading of these network resources are complete. The result: Flicker in the end result! The correct thing is to wait using the delayRender API, but this is not very obvious. Now Remotion comes with built-in components that will wait until assets are loaded.

Bonus: ESLint rule

ESLint rule 'warn-native-media-tag'

To prevent shooting yourself into your foot, Remotion now comes with an ESLint rule that warns if you use the native <img>, <iframe>, <video> or <audio> tags. These tags work better when you use the Remotion-wrapped versions instead.

Upgrading is easy

Simply type

npm run upgrade
npm run upgrade

in your project and you'll get all the newest Remotion packages! Maybe you also noticed that there is a banner in the editor that notifies you when an upgrade is available.

· 2 min read
Jonny Burger

I've been using After Effects for many years, but it's always been a dream of mine to code my videos instead. In the React ecosystem, I am used to being able to take advantage of powerful composition, reusability, to be able to customize the experience for every user. To use scripts, linters and external dependencies to make my life easier as a coder.

So as a proof of concept I tried to make a trailer for my AnySticker app1 in React, and to render each frame using Puppeteer and stitch them together using FFMPEG.

The final result was a video that I thought was the best one I created yet. And I realized there was something to the idea.

This is my attempt to create a tool for the community that allows you to write videos in React. Obviously this is a huge undertaking, so this is not a product with hundreds of different features built in. Rather, following the React philosophy, this is an attempt to create a minimal fundament for rendering videos in React. Basically, you get a blank canvas, and you create your motion graphics using existing web technologies built into the browser and your favorite external libraries. Remotion is so minimal in fact, it consists of only 5-6 APIs that you need to learn to get started.

To get started is super easy. Assuming you already have Yarn, run:

bash
yarn create video
bash
yarn create video

and then read the documentation. Let me know on Twitter what you think!