Skill Tracer
Skills › HyperFrames › remotion-to-hyperframes › fonts.md

fonts.md

112 lines · skills/remotion-to-hyperframes/references/fonts.md at a595755

Open on GitHub
1# Font translation
2
3Fonts are the dominant non-translation noise floor. Same `font-weight: 800`
4renders perceptibly bolder on HF's `chrome-headless-shell` than on
5Remotion's bundled Chromium when there's no real font installed. Validation
6showed this costs ~0.025 mean SSIM at the noise floor.
7

Font translation

Fonts are the dominant non-translation noise floor. Same font-weight: 800 renders perceptibly bolder on HF's chrome-headless-shell than on Remotion's bundled Chromium when there's no real font installed. Validation showed this costs ~0.025 mean SSIM at the noise floor.

Raw lines 1 to 7
8## Pattern: `@remotion/google-fonts/<Family>`
9
10```tsx
11import { loadFont } from "@remotion/google-fonts/Inter";
12loadFont("normal", { weights: ["400", "800"] });
13```
15Translate to a `<link>` tag in `<head>`:
17```html
18<head>
19 <link rel="preconnect" href="https://fonts.googleapis.com" />
20 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
21 <link
22 href="https://fonts.googleapis.com/css2?family=Inter:wght@400;800&display=swap"
23 rel="stylesheet"
24 />
25 <style>
26 body {
27 font-family: Inter, sans-serif;
28 }
29 </style>
30</head>
31```
33Pull the family name and weights from the import path and `loadFont`
34arguments. HF's compiler inlines the Google Fonts CSS at render time, so
35you don't pay a network round-trip per render.

Pattern: @remotion/google-fonts/<Family>

import { loadFont } from "@remotion/google-fonts/Inter";
loadFont("normal", { weights: ["400", "800"] });

Translate to a <link> tag in <head>:

<head>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link
    href="https://fonts.googleapis.com/css2?family=Inter:wght@400;800&display=swap"
    rel="stylesheet"
  />
  <style>
    body {
      font-family: Inter, sans-serif;
    }
  </style>
</head>

Pull the family name and weights from the import path and loadFont arguments. HF's compiler inlines the Google Fonts CSS at render time, so you don't pay a network round-trip per render.

Raw lines 8 to 36
37## Pattern: local fonts via `@font-face`
39```tsx
40import { Font } from "remotion";
42Font.loadFont("/MyFont.woff2", "MyFont");
43```
45Translate to a `@font-face` rule:
47```html
48<style>
49 @font-face {
50 font-family: "MyFont";
51 src: url("assets/MyFont.woff2") format("woff2");
52 font-weight: 400;
53 font-style: normal;
54 }
55</style>
56```
58Copy the font file into `hf-src/assets/` next to the HTML.

Pattern: local fonts via @font-face

import { Font } from "remotion";

Font.loadFont("/MyFont.woff2", "MyFont");

Translate to a @font-face rule:

<style>
  @font-face {
    font-family: "MyFont";
    src: url("assets/MyFont.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
  }
</style>

Copy the font file into hf-src/assets/ next to the HTML.

Raw lines 37 to 59
60## Pattern: system font fallback (no font load)
62```tsx
63<div style={{ fontFamily: "Helvetica, Arial, sans-serif" }}>...</div>
64```
66Same string in HF — but be aware: on Linux without a real Helvetica
67installed (typical CI environment), Remotion and HF fall back to
68_different_ sans-serif system fonts because they bundle different
69Chromium versions. This is the noise floor: ~0.025 mean SSIM cost,
70visible as different stroke widths at large font weights (800+).
72If matching the Remotion render exactly matters for a specific
73fixture, load the same font explicitly — don't rely on system
74fallback.

Pattern: system font fallback (no font load)

<div style={{ fontFamily: "Helvetica, Arial, sans-serif" }}>...</div>

Same string in HF — but be aware: on Linux without a real Helvetica installed (typical CI environment), Remotion and HF fall back to different sans-serif system fonts because they bundle different Chromium versions. This is the noise floor: ~0.025 mean SSIM cost, visible as different stroke widths at large font weights (800+).

If matching the Remotion render exactly matters for a specific fixture, load the same font explicitly — don't rely on system fallback.

Raw lines 60 to 75
76## When in doubt: use Inter
78Inter renders identically across Chromium versions and is free.
79Translate any "system sans-serif" Remotion comp to Inter when you
80need to minimize font drift in the validation harness.

When in doubt: use Inter

Inter renders identically across Chromium versions and is free. Translate any "system sans-serif" Remotion comp to Inter when you need to minimize font drift in the validation harness.

Raw lines 76 to 81
82## Font loading and `delayRender`
84Remotion uses `delayRender()` to defer the first frame until fonts
85load. HF's compiler inlines Google Fonts at compile time and waits
86on `@font-face` readiness via the Frame Adapter pattern — the
87`delayRender` call drops in translation. See [media.md](media.md).

Font loading and delayRender

Remotion uses delayRender() to defer the first frame until fonts load. HF's compiler inlines Google Fonts at compile time and waits on @font-face readiness via the Frame Adapter pattern — the delayRender call drops in translation. See media.md.

Raw lines 82 to 88
89## Multi-weight loading
91When Remotion loads multiple weights:
93```tsx
94loadFont("normal", { weights: ["400", "500", "700", "800"] });
95```
97Inline all weights in the Google Fonts URL:
99```
100?family=Inter:wght@400;500;700;800&display=swap
101```
103Translation rule: enumerate every distinct `font-weight` value
104that appears in the composition's CSS (`font-weight: 800` →
105weight 800 must be loaded). If the Remotion source loads weights
106that aren't actually used, drop them.

Multi-weight loading

When Remotion loads multiple weights:

loadFont("normal", { weights: ["400", "500", "700", "800"] });

Inline all weights in the Google Fonts URL:

?family=Inter:wght@400;500;700;800&display=swap

Translation rule: enumerate every distinct font-weight value that appears in the composition's CSS (font-weight: 800 → weight 800 must be loaded). If the Remotion source loads weights that aren't actually used, drop them.

Raw lines 89 to 107
108## Font subsetting
110Remotion's `loadFont` doesn't subset; HF's compiler doesn't either
111(yet). Don't try to optimize this in translation — it's lossless to
112keep the same weight set as the Remotion source.

Font subsetting

Remotion's loadFont doesn't subset; HF's compiler doesn't either (yet). Don't try to optimize this in translation — it's lossless to keep the same weight set as the Remotion source.

Raw lines 108 to 112