Visual_Core
::
Satori_Integration
Generating dynamic graphics without the overhead of headless browsers. From real-time Edge rendering to local batch processing via CLI.
For years, if you wanted to generate an image programmatically—for example, an Open Graph card for a blog post or a dynamic receipt—you had to summon a demon: Headless Chrome. Launching a full browser instance via Puppeteer just to take a screenshot of a <div> is like commuting to work in a tank. It consumes massive amounts of RAM, is slow to boot, and creates significant latency in serverless environments.
Enter Satori.
Created by Vercel, Satori is a lightweight library that converts HTML and CSS (JSX) into SVG. It doesn’t use a browser. Instead, it utilizes the Yoga layout engine (the same one used by React Native) to calculate geometry. It runs on the Edge, in Node.js, and even directly in the browser.
Here is a breakdown of where and how we can deploy this visual protocol.
01 :: The Edge Protocol (Dynamic Web OG)
This is the configuration currently running on arfour.xyz. The objective is to generate a unique social preview image for every URL slug on the fly, without manually designing or storing thousands of PNG files on a server.
How it works
When a social crawler (like Discord, Twitter, or LinkedIn) requests a link, it hits a specific meta tag. This triggers an Edge Function on the server. The function wakes up, accepts the page title as an argument, and passes it into a Satori template.
Satori calculates the layout in milliseconds and outputs an SVG string. Since social networks require raster images, we pipe that SVG through a tool called Resvg to convert it into a crisp PNG. The entire process happens in the blink of an eye, serving a custom image that never existed until it was requested.
// src/pages/og/[...slug].png.ts
import satori from "satori";
import { Resvg } from "@resvg/resvg-js";
export const GET = async ({ params, props }) => {
// 1. Generate SVG from JSX
const svg = await satori(
{
type: 'div',
props: {
style: { display: 'flex', color: 'white', background: '#18181b', width: '100%', height: '100%' },
children: [
{ type: 'h1', props: { children: props.title } }
]
}
},
{ width: 1200, height: 630, fonts: [...] }
);
// 2. Convert to PNG
const resvg = new Resvg(svg);
const pngBuffer = resvg.render().asPng();
// 3. Serve Response
return new Response(pngBuffer, {
headers: { "Content-Type": "image/png" }
});
};02 :: The Batch Protocol (Local & CLI)
Satori is not limited to the web; it is simply a Node.js library. This makes it a formidable tool for building CLI automation scripts.
Imagine organizing a conference or managing a large team. You have a CSV file with 500 attendees, and you need to generate a personalized “Welcome Card” or “Access Badge” for each one to print or email.
The Workflow:
- Data Source: Your script reads a local dataset (JSON or CSV).
- Iteration: It loops through every entry.
- Render: It injects the specific data (Name, Role, ID) into the visual template.
- Rasterize & Save: It saves the output as
badge_username.pngdirectly to your hard drive.
This approach runs locally on your machine. There are no server costs, no API limits, and you can generate hundreds of assets in seconds.
// scripts/generate-badges.js
import fs from "node:fs";
import satori from "satori";
import { Resvg } from "@resvg/resvg-js";
// Mock Data
const users = [
{ name: "Arfour.xyz", role: "Frontend Architect" },
{ name: "System Admin", role: "Security Ops" },
];
async function generate() {
const fontData = fs.readFileSync("./fonts/Outfit-Bold.ttf");
for (const user of users) {
// 1. Create SVG
const svg = await satori(
{
type: "div",
props: {
style: {
display: "flex",
flexDirection: "column",
padding: 50,
backgroundColor: "#000",
color: "#fff",
},
children: [
{
type: "h2",
props: {
children: user.name,
style: { fontSize: 60 },
},
},
{
type: "span",
props: {
children: user.role,
style: { color: "#06b6d4" },
},
},
],
},
},
{
width: 800,
height: 400,
fonts: [{ name: "Outfit", data: fontData, style: "normal" }],
},
);
// 2. Save to Disk
const pngBuffer = new Resvg(svg).render().asPng();
fs.writeFileSync(
`./output/${user.name.replace(/\s+/g, "_")}.png`,
pngBuffer,
);
}
}
generate();03 :: Beyond Open Graph
Since Satori outputs standard SVG, the use cases extend far beyond link previews.
A. Dynamic Certificates Instead of struggling with PDF generation libraries—which are notoriously difficult to style—you can use standard CSS Flexbox to design certificates. Once a user completes a course, the system generates a high-res image with their name and date, which can then be embedded into a PDF or sent directly.
B. Email Headers Email clients (Outlook, Gmail) are terrible at rendering modern CSS. Instead of fighting with table layouts, you can generate a personalized header image for each recipient (e.g., “Hello Petr, here is your weekly report”) using Satori. You embed a single image, guaranteeing pixel-perfect presentation across all clients.
C. Server-Side Charts
If you need to generate simple bar or line charts on the server but don’t want to load heavy charting libraries that rely on the DOM, you can build them using simple divs and Satori. It is lightweight, fast, and SEO-friendly.
! System warning :: engine limitations
Satori is not a browser; it relies on the Yoga layout engine. To avoid render failures, strict adherence to these protocols is required:
- Flexbox Only: Yoga explicitly does not support CSS Grid. You must design strictly using Flexbox primitives.
- CSS Subset: Advanced properties (animations, complex pseudo-elements,
calc()) are ignored.- Binary Fonts: System fonts are inaccessible. You must load font files as
ArrayBufferdata and inject them into the config.
System verdict
Satori represents a paradigm shift, moving visual generation from a “heavy infrastructure task” to a simple function call.
By automating asset creation, we effectively eliminate the dependency on marketing teams for repetitive graphic tasks. No more manual resizing or “text-change” tickets. Whether running on the Edge or via CLI, it turns raw data into production-ready pixels instantly.