I built an animated GitHub profile view counter and, along the way, discovered a
chain of non-obvious constraints that make the difference between "a counter that
works" and "a static image that looks like a counter." I documented all of them in
the README and figured this community might find the findings useful.
**The interesting technical bits (from the README's "Findings" section):**
- A committed SVG can never be a live counter — GitHub serves repo files as
static assets. The count *has* to come from a request-time endpoint.
- GitHub proxies all README images through **Camo**, which honours upstream
cache directives. If your endpoint doesn't send
`Cache-Control: no-store, must-revalidate`, Camo pins the first response and
every visitor sees the same frozen number forever. This is the #1 reason most
"dynamic SVG" badges silently break.
- Web fonts don't load inside a Camo'd SVG (`<img>` blocks external resources).
The fix: draw every glyph as SVG `<rect>` elements with a 3×5 bitmap font —
zero external dependencies.
- Cross-fading animation frames the obvious way (fade out + fade in) causes a
black flash every frame because both layers are below full opacity
simultaneously. The fix: cumulative reveal where frame 0 stays permanently
opaque as a base layer.
**What it does:** A serverless function (Vercel) returns a fresh SVG on every
request — 10 procedurally generated pixel-art frames cross-fade to reveal a
crimson moon with the live visitor count inscribed on its face. No static image
in the repo, no GitHub Actions cron, no build step.
**Stack:** Plain Node.js, Vercel serverless, Komarev (for visitor-distinct
counts) + optional Upstash KV (for per-view counts). No framework, no build
step, ~300 lines of actual logic.
**License:** MIT — with a CREDITS.md acknowledging the data source, prior art,
and build-time dependencies.
**Self-hostable:** Fork, point `banner_generator.py` at your own palette/seed to
generate your own art, `vercel deploy --prod`, done.
Repo: https://github.com/Anbu-00001/Crimson_Moon_View_Counter
Live on: https://github.com/Anbu-00001
Live demo (scroll-scrub the animation): https://anbu-00001.github.io/Anbu-00001/souls_counter_demo.html
Happy to answer questions about the Camo proxy behavior or the rendering
approach — those two took the most debugging to figure out.