Dynart
Images generated on the fly using React and Resvg. Inspired by vercel/og.
Background
This proof of concept explores generating dynamic og images on the fly. This initially arose from discussions with @magusnn a few months ago and resurged with the announcement of vercel/og
.
The @vercel/og
package remains closed source at the time of this writing, and you can learn more about it in their blog post. After digging in a bit, I wagered we could build a similar service using a custom stack sharing similar foundations.
Technical Design
I wrote this fairly quickly over the course of a few hours, so its definitely not ready for production. However it does outline a possible path forward to implement this service on a larger scale. An edge network could be the optimal approach for this, even though I am not using one here.
Another approach being considered for scaling the service, is to isolate it as a component of a distributed system, where edge nodes request a new image and pipe through the response.
Api Routes with Next
The server routes are served by Next+Vercel. Its a few standard routes responsible for the image generation. You can request either a random image (from a select few models) using /random
or a specific type of image using /s/<generator name>
.
The api routes are also guarded using a rate-limiter. Its based off Next's api-routes with rate limiter example which you can set up with:
npx create-next-app --example api-routes-rate-limit api-routes-rate-limit-app
An lru-cache
is used to implement a simple rate limiter for API routes (Serverless Functions).
curl <http://dynart.edede.ca/api/img/random> -I
HTTP/1.1 200 OK
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
curl <http://dynart.edede.ca/api/img/random> -I
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
Arte
Spinning this up so quickly was possible due to the previous work done in the Arte project. Arte is a generative art project, which generated svg variants based on encodings of underlying models. This infographic explains the system best:
The Arte generators can output an svg
string, rendered by ReactDOMServer.renderToStaticMarkup
. I ported over the code for Arte, and adapted it slightly. In the future this could be reworked entirely for dynart.
Resvg
The final piece of the puzzle is converting the generated svg
string into an image. @vercel/og
abstracts this away I suspect behind ImageResponse
. vercel/satori converts the passed JSX into an svg
string, and then ImageResponse
generates the image. In this case, we already have the generated svg
string, and can avoid @vercel/og
entirely by using resvg-js to convert the svg
into an image. This is the same package used by Vercel.