Skip to content

Dynamic per-poll OG images #75

@zhiganov

Description

@zhiganov

Problem

Since #46 (Open Graph metadata) shipped, poll links unfurl with the correct title + description but no image — og:image was removed because the only available asset (/og-image.png) is a 512x512 square that rendered as a giant awkward card on Telegram/Slack/Discord.

A generic wide (1200x628) logo/brand image would be a small improvement, but the real win is per-poll images that show the scheduling context at a glance.

Proposed

Render a dedicated image per poll, served at something like `GET /p/:did/:rkey/og.png` and referenced from the injected `og:image` tag in the poll route's HTML.

Content ideas (open to iteration):

  • Poll title in large type
  • Small heatmap strip showing response density across the poll's dates
  • Date range + timezone
  • Open/Scheduled badge (with the scheduled time when applicable)
  • Avails wordmark in a corner

Implementation notes

  • Rendering stack: `@vercel/og` (Satori + resvg) is the common serverless choice — JSX-to-PNG, runs without a headless browser. Node `canvas` works but needs native deps that might not be clean on Nixpacks. Evaluate both.
  • Caching: generate once, cache the PNG keyed on `(did, rkey, record CID)`. Serve stale while revalidating on poll edits / status changes.
  • Response heatmap: the poll record doesn't include responses — either fetch them at image-render time (adds PDS round-trip) or pre-compute a small stat struct.
  • Fallback: if image rendering fails for any reason, the poll route should still serve the HTML without og:image — never break the text preview.
  • Railway constraints: generation must stay well under the platform's request timeout; cache hit must be very fast (it's on the crawler hot path).

Not this issue

  • Static 1200x628 brand image — could be a quick predecessor PR (just design the asset + update template), filed separately if wanted.
  • Per-response avatars / Bluesky handles — out of scope, privacy considerations.

Follows up #46.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions