seedkin

On the server

Serve avatars as image URLs, such as /avatar/123, so any page or app can show them with a plain <img>. Seedkin makes no requests and keeps no state, so it runs anywhere JavaScript does, including edge runtimes.

A route handler

In Next.js (App Router), as app/avatar/[id]/route.ts:

import { botling } from 'seedkin/botling';

export async function GET(_request: Request, { params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  return new Response(botling(id, { size: 96 }), {
    headers: {
      'Content-Type': 'image/svg+xml',
      'Cache-Control': 'public, max-age=31536000, immutable',
    },
  });
}

Any framework that returns a web Response (Hono, Remix, SvelteKit, Astro, Bun, Deno, Cloudflare Workers) looks the same. In Node without one:

import { createServer } from 'node:http';
import { botling } from 'seedkin/botling';

createServer((req, res) => {
  const id = decodeURIComponent(new URL(req.url ?? '/', 'http://localhost').pathname.slice(1));
  res.writeHead(200, { 'Content-Type': 'image/svg+xml', 'Cache-Control': 'public, max-age=31536000, immutable' });
  res.end(botling(id, { size: 96 }));
}).listen(3000);

Caching

A seed's picture never changes within a major version, so the response can be cached for good. Put the major version in the path (/avatar/v2/123) so that an upgrade to 3.0 gets new URLs rather than a mix of old and new pictures. Read options such as size or frame from the query, check them, and they cache the same way: Seedkin throws a RangeError on a bad one, which you can turn into a 400.

Privacy

An avatar URL puts its seed in logs, analytics and the Referer header. Seed with an opaque id, never an email address, so the URL gives nothing away.

Email

Many email clients, Gmail among them, don't show SVG images. For email, convert the SVG to a PNG on the server, for example with @resvg/resvg-js, and serve that instead.