尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Animated og:video link previews rendered in a Cloudflare Worker, streamed faster than realtime, cached after the first view. Hosted at og.video.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Animated og:video link previews, rendered inside a Cloudflare Worker and streamed out faster than they play. No headless browser. Each distinct video renders once and is cached after that.
Just want a URL? Use the hosted version at og.video:
<meta property="og:video" content="https://og.video/v1/title-card.mp4?title=We%20shipped%20it&kicker=Launch&brand=acme">
<meta property="og:video:type" content="video/mp4">
<meta property="og:video:width" content="960">
<meta property="og:video:height" content="504">
<meta property="og:image" content="https://og.video/v1/title-card.png?title=We%20shipped%20it&kicker=Launch&brand=acme">
request ──► Worker
├─ edge cache (Cache API) hit ─────────────► MP4, ~2 ms
├─ R2 hit ─────────────────────────────────► MP4, streamed
└─ miss:
1. write the prefix: the opening frames, pre-encoded once per prefix key
2. render the rest live, one fragment per frame:
scene timeline (GSAP on plain objects)
→ Rust/wasm compositor (tiny-skia + SIMD): cached sprites and layers,
transformed draws, masks, blend modes, blur, dot fields, images,
converts to I420, flags unchanged 16×16 blocks
→ x264 (wasm, SIMD128 kernels) encodes only what changed
→ moof/mdat fragment → response
3. store the finished file in R2 + the edge cache
npm install github:runtypelabs/og-video
// src/index.js
import { createHandler, kvStore } from 'streaming-og-video/worker'
import { titleCard, diagram } from 'streaming-og-video/scenarios'
export default createHandler({
scenarios: [titleCard, diagram],
bucket: 'RENDERS', // R2: rendered videos, posters, prefixes
store: kvStore({ kv: 'STATE', r2: 'RENDERS' }), // KV: stored params for short URLs
secret: 'OGV_SECRET', // signed URLs (&sig=) and write token
rateLimit: 'RENDER_LIMIT', // per-IP limit on cache misses
})
examples/worker/wrangler.jsonc has the bindings. Wasm and fonts load through wrangler's default module rules, so there's nothing else to configure. With a secret set, inline params must be signed. Mint URLs with videoUrl(origin, scenario, params, { secret }).
| Request | Returns |
|---|---|
GET /<scenario>.mp4?<params> | The video, streamed on a miss |
GET /<scenario>.png?<params> | The poster frame, for og:image |
GET /<scenario>?<params> | A share page with og:video and og:image tags |
GET /<scenario>.json?<params> | Resolved params, content id, links, and a packed ?s= URL |
POST /<scenario> (JSON body) | Stores params and returns /<scenario>/<id>.mp4 (the id is a content hash) |
PUT /<scenario>/<name> | Stores params under a name (needs the bearer token) |
Routes can live under a version prefix: createHandler({ basePath: '/v1' }) serves /v1/<scenario>.mp4 and uses the prefix in every link it returns (videoUrl(..., { basePath }) does the same). Add legacyPaths: true to keep serving the unprefixed routes too. og.video uses /v1.
Params are query strings. Arrays and objects go as JSON, or as ?s=: canonical JSON, deflated and base64url-encoded, about 3× shorter. Query keys the scenario doesn't declare, such as utm_source, are ignored and don't affect the cache key.
| Primitive | Role |
|---|---|
| Workers | Rendering, encoding and streaming |
| Cache API | Per-location copy of finished videos |
| R2 | Global, durable store for videos, posters and prefixes. Strongly consistent, so it also backs stored params right after a write |
| Workers KV | Stored params, read at the edge. An R2 fallback covers KV's propagation delay |
| Rate Limiting | Per-IP limit on renders and public writes |
Analytics Engine (via onEvent) | One event per request: cache result, client, bytes, frames, degraded renders |
A scenario is a pure function of (params, frame index). Rasterize in setup, and describe each frame as cached layers plus live overlays:
import { gsap } from 'gsap'
import { defineScenario, p, rrect } from 'streaming-og-video'
export const hello = defineScenario({
id: 'hello',
version: 1,
video: { width: 1200, height: 630, fps: 30, frames: 150 },
params: p.object({
title: p.string({ max: 80, default: 'Hello' }),
accent: p.color({ default: '#3b82f6' }),
}),
prefix: { frames: 20, key: (q) => q.accent }, // frames 0-19 only depend on the accent
meta: (q) => ({ title: q.title }),
setup(ctx, q) {
const bar = ctx.path(rrect(0, 0, 400, 8, 4))
const title = ctx.sprite([{ cmds: ctx.text.run('semibold', q.title, 72).cmds(100, 340), color: '#fff' }], { margin: 20 })
const s = { bar: { w: 0 }, title: { opacity: 0, blur: 8 } }
const tl = gsap.timeline({ paused: true })
.fromTo(s.bar, { w: 0 }, { w: 1, duration: 0.6, ease: 'expo.out' }, 0.1)
.fromTo(s.title, { opacity: 0, blur: 8 }, { opacity: 1, blur: 0, duration: 0.4, ease: 'power3.out' }, 0.7)
return {
state: (i) => (tl.seek(i / ctx.fps, false), s),
draw(g) {
g.layer('bg', () => g.clear('#111')) // cached: restored by memcpy
g.fill(bar, q.accent, { transform: [s.bar.w, 0, 0, 1, 100, 380] }) // live overlays
g.draw(title, s.title)
},
dispose: () => tl.kill(),
}
},
})
Iterate locally:
npx sogv stills src/hello.js#hello --params '{"title":"Hi"}' --at 0.5,1,last # PNGs to look at
npx sogv render src/hello.js#hello -o out/hello.mp4 --png
npx sogv bench src/hello.js#hello # ms per full render
npx sogv prefix src/hello.js#hello --vary '[{"title":"Another title"}]' # checks the prefix contract
The skill covers the mental model, the full API (authoring) and deployment (deploy). It's written for coding agents, and people can read it too. To use it with Claude Code, copy skills/streaming-og-video into .claude/skills/.
| Id | What it is | Try it |
|---|---|---|
title-card | An announcement card: a grid reveal, a pixel wordmark that flips into the header, a typed kicker, then the title words blur in | og.video/v1/title-card |
diagram | An architecture card: lanes of nodes wipe in, edges draw, packets travel between them, and stats count up. Nodes come from JSON or the graph shorthand: Agents: Bot@bot | Surfaces: Slack@chat | og.video/v1/diagram |
kinetic | Kinetic type: a headline rises word by word, and *starred words* land in the accent colour with a highlight block | og.video/v1/kinetic |
metric | A big number rolls up like an odometer over a trend line that draws itself, with a delta pill | og.video/v1/metric |
bars | A bar chart grows in a staggered wave, values count up, the highlighted bar lands in the accent, and an average line sweeps across | og.video/v1/bars |
release | Release notes: a huge version number rises behind the title, and each item slides in with its icon | og.video/v1/release |
dot-matrix | A product card whose blue panel cycles through dot-matrix icons (search, brand, map, monitor, scrape, code, chart), each dissolving in and out dot by dot with a pixel-font label | sogv render dot-matrix |
falling-words | A dot and a label; about a hundred small words rain in at random angles, tumble and pile up (precomputed rigid-body physics), then drop out to loop | sogv render falling-words |
All of them take accent, and kinetic, metric, bars, release and the six from terminal down also take theme=light. The last three are new in v2 and not yet on og.video, and neither is dither. GET /<scenario>.json lists the resolved params with their defaults.
src/ the library (plain ESM + JSDoc; types in types/)
scenario.js defineScenario
schema.js p.* params schema (Standard Schema compatible)
params.js canonical JSON, content ids, packing, signatures
gfx.js the Scene/Frame drawing API over the compositor
physics.js a deterministic 2D rigid-body world, for precomputed motion
text.js shaping and layout with Chrome's rounding rules (fontkit)
engine/ per-isolate engine, x264 glue, fMP4 prefix + tail, PNG, image params
worker/ createHandler, KV/R2 store, share page
node/ Node engine and the sogv CLI
scenarios/ built-in scenarios
crates/ogfx/ the Rust compositor (npm run build:ogfx)
x264/ x264 wasm build (npm run build:x264), with SIMD128 kernels checked against x264's C
wasm/, fonts/ built artifacts, Inter and JetBrains Mono
gallery, falling-words) take 0.3–0.5 s per 7 s at 60 fps locally, and falling-words is about 3.5 MB. Lower video.fps for scenes like that if size or render time matters more than smoothness.mb_info, and enough speed in wasm.sogv prefix build), so even the first request for a key is instant.MIT, except wasm/x264.wasm, which is GPL-2.0-or-later. That matters if you redistribute builds that include it; see NOTICE.md. Inter is OFL-1.1 and Twemoji outlines are CC-BY 4.0.
gallery | A title block beside a two-column wall of thumbnails that scrolls without end. Thumbnails are images URLs (PNG, JPEG, WebP), or built-in illustrated tiles | sogv render gallery |
terminal | A shell session replays: commands type out behind a block cursor, spinners resolve to a check or cross, and output prints in. lines mark commands with $, successes +, failures !, muted >, accent *words* | sogv render terminal |
pr | A pull request: title and state pill land, diff lines type in over red and green rows, the +/- counters tick, and a Merged (or Closed) stamp lands | sogv render pr |
repo | A repo or package card: the star count rolls up, the language bar fills, topic pills pop in, and an optional star history draws with milestone ticks | sogv render repo |
transcript | An agent run replays: user bubbles, tool calls whose spinners resolve to a check (or a red cross for r!: failures), typing dots, then the answer. chat=u: … | t: tool(args) | r: result | a: answer | sogv render transcript |
grid | A results grid (evals, test matrices): tiles flip in a wave to pass, warn or fail with a glyph each, row tallies land, and a pass count rolls up | sogv render grid |
dither | A dithered aurora in the accent colour rises in Bayer order and keeps flowing at 60 fps, re-dithered every frame against a fixed matrix; the title dithers in, then resolves to solid type. After dithercn's Aurora | sogv render dither |
race | A bar chart race: horizontal bars swap ranks across periods as values count, the axis rescales to the leader, and a big period counter rolls over | sogv render race |