Bring stop motion vibes to Remotion

Marcus Stenbeck

Marcus Stenbeck / November 15, 2021

4 min read

Smooth motion at 60 frames per second is all over the place. It has a buttery smooth feeling, like in this clip below.

0:00 / 0:02

Mmm... smooth.

But silky smooth isn't the only game in motion graphics. On high production YouTube channels like Cheddar and Vox you might notice that not everything moves at the same pace. In their videos there are often elements that move with a choppier feeling than its surroundings.

Take a look at the clip below and notice how the ball is still moving at a smooth 60 fps while the line is revealed at 20 fps.

0:00 / 0:02

Deliberately reducing the smoothness of motion of elements adds an old-school, stop motion or hand crafted vibe.

Reduce the frame rate in Remotion

Animations in remotion usually depend on an interpolator. The raw frame rate value from useCurrentFrame() is used to drive the animation.

import { useCurrentFrame, useVideoConfig, interpolate, Easing } from 'remotion';

const MyElement = () => {
  const frame = useCurrentFrame();
  const { durationInFrames: duration } = useVideoConfig();

  const finalWidth = 200;
  const width = interpolate(frame, [0, duration], [0, finalWidth], {
    easing: Easing.inOut(Easing.quad),
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp'
  });

  return (
    <div
      style={{
        backgroundColor: 'salmon',
        height: 100,
        width
      }}
    />
  );
};

The value from useCurrentFrame() updates 60 times per second for a 60 fps video. In order to get a lower frame rate look we need to produce a value that changes less than 60 times per second. This is called posterizing in Adobe After Effects. Let's create a function called posterizeFrame() that gives us what we want.

function posterizeFrame(
  currentFrame: number,
  desiredFps: number,
  originalFps: number
): number {
  return (
    (originalFps / desiredFps) *
    Math.floor(currentFrame * (desiredFps / originalFps))
  );
}

Added to the example component it looks like this.

import { useCurrentFrame, useVideoConfig, interpolate, Easing } from 'remotion';
import posterizeFrame from './posterizeFrame';

const MyElement = () => {
  const frame = useCurrentFrame();
  const { fps, durationInFrames: duration } = useVideoConfig();

  const desiredFps = 20;
  const posterizedFrame = posterizeFrame(frame, desiredFps, fps);

  const finalWidth = 200;
  const width = interpolate(posterizedFrame, [0, duration], [0, finalWidth], {
    easing: Easing.inOut(Easing.quad),
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp'
  });

  return (
    <div
      style={{
        backgroundColor: 'salmon',
        height: 100,
        width
      }}
    />
  );
};

Hope you'll find some creative uses for this trick!

I've put the source code for the clips in the beginning on this article here: Codesandbox