Shu Profile picture
Oct 10 19 tweets 7 min read
Some tech details and stories behind @vercel’s OG image generation and Satori lib...

There’s been a demand to generate dynamic and fancy OG images for a long time. And we have some solutions but they all seem to be a little “limited”.
...If using a canvas-like (e.g. Skia) based solution, you don’t have fancy/dynamic things like layouts. Also no nice APIs for humans.

With a browser-based solution you get all the fancy things and human-friendly, declarative APIs but too expensive, dev unfriendly :(
Then, SVG just has the perfect balance in API design and perf.

It’s a declarative image language that every web developer is familiar with: HTML/CSS. It’s been there since 1999 and it’s so fast to render (rasterize).

The only missing thing is layout. SVG doesn’t have it.
But since HTML/CSS and SVG are so close, can we calculate CSS’ layout and translate all the resolved styles to SVG?

Then the solution neutrally came out: React Native already did the first part for us with Yoga, and it’s fast.
yogalayout.com is not an advanced layout engine, it has flexbox only but it’s good enough for image generation. We don’t need advanced layouts, breakpoints, interactive elements, or animations.

I then ported it to be runnable on the web as WASM: github.com/shuding/yoga-w…
CSS consists of layout and styles. A layout engine can compute absolute sizes and positions from these. Then we can easily get the SVG markup based on these absolute sizes and positions + styles. The concept is pretty simple.
...and then, I implemented this proof-of-concept in January, and it worked pretty well:

...as a person who works with React every day, I realized that we can directly use JSX to make it easier. Here’s the start of the lib:
One thing that gave me pressure is that implementing the CSS spec even a subset is very hard.

But we are still optimistic for the OG image use case — you don’t need to render the same as the browser, the key is to make a card locally & look the same on prod. It’s not difficult!
...and with this approach, it’s very easy and lightweight to do local development of the card. Thanks to github.com/RazrFalcon/res… and github.com/yisibl/resvg-js, we added SVG → PNG to close the loop.
Since both Satori and Resvg are so lightweight, a lot of opportunities can be unlocked such as running on the Edge. So I spent some time optimizing both WASM libs by enabling SIMD & github.com/WebAssembly/bi… etc. I saw a 2~3x perf boost in Resvg.

vercel.com/blog/introduci…
And I patched an optimization to the upstream Resvg library as well, doubling the speed for certain cases: github.com/RazrFalcon/res…
With all these, we then spend a couple of months testing it internally with vercel.com/docs. All the cards that look like these are powered by Satori now:
... and SWR’s docs too. This one is using a subset Japanese font from Google Font using `?text=` API. So we don’t need to include the huge CJK font file, and we can render many languages dynamically on the fly:

developers.google.com/fonts/docs/get…
swr.vercel.app/ja/docs/condit…
We did the same for emojis. All emojis are loaded as images from CDNs on the fly, when you actually need to render them! (Did you know that AppColorEmoji.ttf is 44MB?)

Also got a couple of style variants for you:
And finally, all the tickets from #nextjsconf are generated using this little library too. The traffic was huge but we didn’t have a single hiccup when generating these images:

Overall I am happy to see this approach proved to work well during all our internal tests, and I love the API design. In fact, I don’t think it can be simpler:
That’s everything for now — try it out and give us feedback! ❤️

*AppleColorEmoji.ttf

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Shu

Shu Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @shuding_

Jun 21
The @vercel dashboard has been growing with new features and more engineers joining the team. How do we keep it fast?

Here’s a thread... The result of Vercel’s dash...
1️⃣ Find the bottleneck. 1ms doesn’t really make a difference until it’s executed 1,000 times. So the first thing we did was to identify the hot paths. This will include both JavaScript and HTML/CSS.
We have a utility `isLoggedIn()` to check if the current visitor is logged in by reading `document.cookie`. And it turned out to be called 400+ times in one render pass.

Frequent cookies/localStorage/etc. accesses are expensive. Avoiding that made it ~180ms faster to render: After optimizing frequent d...
Read 16 tweets
Dec 28, 2021
I made github.com/shuding/cobe a while ago: a 5kB globe lib, and it improved our page performance* by almost 60%.

How is it created? Time for a WebGL & shader thread!
First of all, today's popular WebGL libs are usually sized at hundreds of kilobytes, but there are also lightweight choices such as github.com/vaneenige/phen… and github.com/xem/W. All of them are wrappers on top of the WebGL standard APIs, but with different feature sets.
Earlier this year we were using a Three.js based solution. The globe is a SphereGeometry that loads the full world map JPEG as texture. Every visitor will spend ~40kB loading that image, and it still felt blurry so we couldn't optimize it further. Image
Read 18 tweets
May 23, 2020
Shower thought 1: I wish we can enable only a subset of CSS properties (strict mode?), which ensures the layout calculation to be one or two passes (one for sizes and one for positions).
Most auto layout algorithms (such as iOS Auto Layout and CSS Grid) are kinda unpredictable and unsplittable. They all have this simulation procedure:

constraintsResolved ? finish() : goToStep1()

...like a physics engine. Typography and layout are hard.
Shower thought 2, somehow related: I wish we can also get another subset of CSS properties that are unrelated to the layout, and we can use that separately.

That's called shader in computer graphics. The concept is not only efficient and beautiful, but also much more powerful.
Read 5 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us on Twitter!

:(