Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Satori – Convert HTML and CSS to SVG (github.com/vercel)
244 points by steventey on Oct 10, 2022 | hide | past | favorite | 59 comments



(Warning: bit of a tangent)

Conceptually, I love this.

Mainly because for some reason it irritates me than screenshots are bitmaps.

Nearly everything in a user interface or document is vector-based, except for photographs and natural-media illustrations.

The ability to capture part of a webpage as an SVG feels like a step forwards in preserving vector content.

My real wish is that OS interfaces would be just as vector-based, where rendering to bitmap was just something the OS handled at the end. So a screenshot in macOS or Windows would similarly always be SVG.


I also wished that was true while working on a fast web-based remote desktop client. Imagine if instead of a video stream via VNC, the UI was HTML or SVG diffs. That would be really fast! RDP does something similar but on a much lower-level.


Yes! IIRC, RDP was originally based on simply sending over the GDI calls -- the Windows draw instructions -- directly.

I've been told it moved away from that as networks got faster, interfaces became more GPU-accelerated, and video encoding became hardware-accelerated, so that now it's not much different from VNC. But I don't know when or to what extent, or if RDP still uses any draw instructions at all.


RDP most likely uses privileged instrumentation to detect screen damage, rather than having to do a full framebuffer scan (or getting framebuffer damage bounds) to identify diffs.


Modern RDP can use hardware h.264 encoding if your GPU supports it, and grabs the output from the framebuffer and streams it like a game streaming app does. I think the default RDP mode still sends the draw commands the old-fashioned way though.


There was a tool called radmin back in the [days before it was heavily commercialized then ruined] that still to this day is the absolute fastest remote desktop setup I’ve ever used. I’d be surprised if it was more than 20ms + network latency.

I suspect that it transferred OS calls.

But even then, that would pale in comparison to SVG diffs from an SVG-native OS.


Yes! Rasters are really portable and convenient and reliably consistent. But it irks me when I have to use rasters. I want vectors everywhere. Generated everything! Quantize it at the very last minute.


Firefox has built-in screenshot support, with the ability to capture just a piece of the screen. How difficult really would it be to save as svg?


macOS used to produce pdf files for screenshots long ago. They switched to pngs, probably because those are easier to share.


PNGs are better for screenshots in most cases both for sharing and for working with the images. The PDFs that Mac OS were generating were a PDF wrapper around a raster screenshot. You can configure the screenshot utility to save in PNG, JPEG, PDF, or TIFF. You can also configure it to save the files to a folder like ~\Pictures\Screenshots instead of dumping them on the desktop.


We used this to generate dynamic share cards for our core web vitals checker tool (plug: https://calibreapp.com/tools/core-web-vitals-checker).

Previously new uncached requests for Open graph share-card images were generated in Chrome, sometimes taking up to 5 seconds to generate. (They were then stored in cache for 12 hrs).

Because we're now using Satori powered og images, they render almost immediately and we don't have to own the problem of having Chrome deployed within the 50mb lambda limitation. I'm pretty happy about that.


You can do something like this via the browser by printing the page to PDF then using Inkscape to convert that to SVG. I documented this here: https://www.checkbot.io/article/web-page-screenshots-with-sv.... There's a few caveats though like box-shadows getting converted into bitmaps and vectors not being grouped in layers like you'd expect, plus it's heavyweight if you're wanting to script it.

I'm curious if Satori could be used to capture in-app client-side screenshots accurately.


There was some Google properties that had some in-page tool to screenshot the page - wonder how they did that one?


It's all client-side apparently, essentially by writing a page renderer in JavaScript. See:

https://stackoverflow.com/questions/4912092/using-html5-canv...

https://github.com/niklasvh/html2canvas


Note that html2canvas has a lot of limitations, since everything that needs to be rendered has to be implemented from scratch using canvas primitives. If you have (IIRC) shadows, more advanced CSS features, certain SVG icons, etc., your output will be pretty broken. I usually have to do DOM cloning and modify/hide the problematic parts before using html2canvas to generate an image.

It’s incredibly annoying that screenshotting isn’t a DOM API, while the functionality is clearly there in DevTools.

Edit: To add to the annoyance, iOS Safari is extremely stingy with canvas memory.


> It’s incredibly annoying that screenshotting isn’t a DOM API

As if we needed more browser fingerprinting vectors.


I recently implemented a similar concept to this for HTML emails at shortwave.com, it only works for the subset of non table emails (which we assume are marketing emails and you want to see the full HTML). It allows us to render stuff wayyy faster (especially on mobile where webviews are not cheap). We don't go all the way to pixel layout because we want to have some responsiveness, but it allows for heavily normalized emails (no more neon yellow on a green background with comic-sans font) and some other neat optimizations.

Didn't use yoga but a from scratch implementation. A good starting point is let's build a browser engine [1]. I hope to blog about it at somepoint.

[1]: https://limpet.net/mbrubeck/2014/08/08/toy-layout-engine-1.h...


Interesting.

I was thinking that this was going to be a crazy amount of layout engine work, but now I look a little closer it appears the layout work is farmed out to yoga [0] (not trying to take away anything from the effort here). So this project is almost a wrapper around running yoga as a renderer and using SVG as a form of backend target?

I say "appears" because the yoga landing page doesn't do a great job of explaining what it does.

EDIT Just looking at some of the information about the font side of things and the naming is a little confusing. You set embedFont=false to use text instead of converting the text to paths. embedFont=true sounds a lot like it will embed the _font_ required to render the _text_ - but it kinda does the opposite.

https://github.com/vercel/satori#font-embedding

[0] https://github.com/facebook/yoga


This seems really cool, but I don't understand the use cases. Someone help me out?


You can basically use this to generate Open Graph images programmatically for your websites really really fast (1-2ms)!

Here's a blog post detailing how this works (using @vercel/og): https://vercel.com/blog/introducing-vercel-og-image-generati...


That's pretty cool. I've hand rolled an og-image generator with Automattic/node-canvas for a static site (Docusaurus) at build time. Edge is cool, but doing it at build time probably makes more sense for static sites.

Looks like using Satori might have been easier to use then having to do all the canvas draw calls (which isn't all that bad either though).

Edit: Nevermind. Actually looks like this wouldn't be self hostable in a github action. vercel/og doesn't run in a node runtime?


Satori does run in node runtime.

vercel/og (a wrapper around Satori) does not support node


Could satori be used to convert a whole webpage into a single SVG image?

Could be a (convoluted?) way to ensure an asset looks the same across all browsers..


No, Satori does not guarantee that the SVG will 100% match the browser-rendered HTML output. That's because Satori implements its own layout engine based on the SVG 1.1 spec. However, Satori generated result (SVG) is stable on all browsers.


This was the use case I had in mind. My company generates exports that are either PDF or PNG/JPEG images from the DOM using Puppeteer. We really wanted to support SVG but every option we explored had pretty significant rendering issues, meaning an entire rewrite in SVG would have been required.


Is the SVG->PNG portion of the show handled by Satori as well, or separately as a part of the Vercel service? Asking because I’ve got code handling that part for this exact use case, which I’d be happy to either eliminate if I can use Satori, or I could clean up and open source my own solution if people would find it useful.


The SVG -> PNG part is handled by @vercel/og. Specifically, it uses Vercel Edge Functions + WebAssembly to handle generating images at the edge.

https://vercel.com/docs/concepts/functions/edge-functions/og...


Thanks! Not to discourage anyone from using Vercel services if they’re interested (y’all are doing great stuff on this and lots of other fronts), but! I’d be remiss if I didn’t point out the unpublished-but-MIT-licensed repo I could find.

For anyone else who’s curious it looks like the pertinent source[1] can be self-hosted. I’m on mobile so I’m gonna limit my peering into the source, but it appears to wrap Chromium to do the PNG generation. Quite a bit different from my solution (which takes SVG-producing JSX and anything producing CSS, and renders to PNG with Sharp).

I’m curious how much overhead using Chromium adds, and whether alternatives like Sharp were considered.

1: https://github.com/vercel/og-image



This is vaguely supported by the browser natively, although you do have to find a way to output the CSS that impacts the SVG itself.

I recently made image export on some SVG charts that you could download as SVG and PNG and I was quite pleased I managed to do it without any library


To be clear, my implementation is build-time only but could easily be adapted to be dynamically server rendered, and probably could be adapted to the browser if the underlying graphics library (Sharp) has WASM support. It does need some awareness of how to get styles which can be made pluggable, but works well with my heavily customized atomic CSS setup generating styles and classes with Fela.

My use cases are all very unusual, but they’d probably make portability a much simpler task because I hobbled together such a weird set of technologies.


Can be useful to generate invoices without dealing with pdf


Converting text to SVG is super hard. Flowing English text is easy enough, but when it comes to Unicode text, with languages such as Arabic and Indian languages, that's when it gets hard. I tried Satori, and it doesn't handle Unicode very well, particularly, combining characters.


Advanced text shaping isn’t implemented yet, where we plan to use Harfbuzz in the future.


Love the project! Sorry to be pedantic, but the claim is technically false. You don't actually support HTML and CSS. You support JSX. As someone who doesn't use React due to the abomination of mixing html and css within the JS, do you have plans to actually support regular HTML and CSS? Or provide a dedicated transpiler? (I'm not familiar with the react ecosystem, so likely one already exists?)


The claim is correct, it supports a subset of HTML and CSS[0]. You can even check the demo page[1], it has examples with HTML and inline styles.

[0]: https://github.com/vercel/satori#html-elements [1]: https://og-playground.vercel.app


[0] "supports a limited subset of HTML and CSS features" (and it still requires jsx-esque syntax with curly braces)

[1] uses JSX

So, yeah, my comment still stands.


JSX here is just a way to represent the HTML information because you can't write HTML in JavaScript. It is NOT required. But with the JSX representation, the lib doesn't have to parse it again, and it's using it as JSON, check the "Use without JSX" section in the docs: https://github.com/vercel/satori#use-without-jsx


Alright, I like this, almost a lot!

In the 90's there was a vector graphics editor called Satori. It was something beyond the understanding of us neighbourhood kids. Something beyond the Paint (and Paintshop Pro). Now let's get back from the nostalgia trip...


Illustrations were created using a vector graphics engine, and then rendered as a bitmap. The brushes looked 'painterly', but they were editable and scalable. So it was possible to take a postage-stamp sized picture and scale it up to billboard size without any loss of resolution.

Watercolor paintings that were actually vectors? Yes, that's exactly what it offered!

There was also some architectural trickery that enabled huge pictures to be loaded into the application on machines with limited RAM.

The nearest software to this now is Synthetik Studio Artist (https://synthetik.com/).


Does the tailwind support work with className declaration?

I would like to use it with existing component library which is built using tailwind without making changes. Is that possible?


They have experimental support for taiwind: https://og-playground.vercel.app/?share=xVRNb9swDP0rgoahl6h2...

Edit: Oh I re-read your comment and you want to know if they support classnames... then my answer is not what you need :(


Interested in this too.


This would have been great for that one time I found a html+css copy-paste script for styled multiple underlined text. Doing it in LaTeX/tikz directly would have taken me hours.

The html+css looked fine, but to get it to a vector image as required by the journal was a pain: print to PDF, find out the lines aren't reproduced correctly, manually find a PDF printer with the right settings, convert to eps


Some of what looks challenging here is handling fonts. SVG would be so much better if there was a way to do font embedding. Even a minimal set of fonts would be amazing. Or if operating systems or libraries for SVG support could all commit to supporting a common set of fonts, like browsers did for web safe fonts. Think of how clunky the web would have been without those.



In my experience, embedded fonts are handled very inconsistently (and often poorly) by different rendering engines.


In particular: if you load an SVG in Chrome via an img tag, the webfont is not run because the environment tries to execute as little as possible. It'll instead use whatever fallback font is present on the system.

Source: I just dealt with this problem a few weeks ago. I also spent all last week implementing a custom OG image in Canvas. Wish Satori had come out a week earlier!


That didn't sound right to me, and I've just checked that the fonts are loading for me. I wonder if you were embedding them in a way that didn't work?

EDIT: here's how I'm doing the embedding

    <style type="text/css">
        @font-face { font-family:FontName; src:url(data:font/otf;base64,T1RUTwAOAIAAAwBgQ0ZGIHKc...AAAAAAAAAAAA=) format("opentype"); }
    </style>
I've passed by the Chrome SVG code a bit, and I've never seen anything to suggest there's an "SVG lite" type version in there. I imagine it's just painted to a bitmap and handed to the compositor like everything else (obviously a massive simplification).


I think you can safely embed data URLs because they’re known to be static. What you can’t generally do is trigger a network waterfall or dynamic evaluation from an img tag’s SVG resource.


Gotcha. When thinking of embedded fonts, my mind goes straight to totally embedded with data-uris (which would have worked around OPs issue above).


PDF has a set of 14 standard fonts that are expected to be available (or an equivalent font) for consistent rendering across platforms.

Of course they support embedding too, so you can use any font (and I think embedding is required to be conformant with Pdf-a - but I’m a little hazy on that).


Is this not reminiscent of Display Postscript?


In Display PostScript the clients emit PS as their interface and for graphical output - its execution of a self-contained page/screen description language from the outset. HTML and CSS are very much not that. I think if anything this is more akin to "Save [this web page] to PDF".


Is Satori including it's own HTML/CSS renderer or relying on a browser behind the scenes?


It does not rely on a browser. Satori itself includes a CSS parser/normalizer, a flexbox layout engine, a text layout engine and a SVG renderer.


So, I am really more courious, with a JS engine is it an almost complete browser (probably excluding some aspects)? Does it include transitions?


Looks great but as far as I know for some reason no site supporting OG previews support SVGs


JSX, not HTML. Hate JSX.




The deadline for YC's W25 batch is 8pm PT tonight. Go for it!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: