Turn Your GitHub Profile Into an Animated Terminal (No Widgets, No Tokens)
Most GitHub profiles are either an empty grid or the same three third-party stat widgets everyone else embeds. This is the self-hosted way: three animated files you generate yourself, committed to a repo you control, kept fresh by a daily robot. It is the exact build running on my profile right now.
The secret repo GitHub never advertises
Create a repo named exactly after your username (so username/username) on GitHub and drop a README.md in it. GitHub renders that README at the top of your profile page. That is the entire unlock, everything else is what you put in it.
Mine is AKCodez/AKCodez, open it in a second tab so you can see every trick in this guide running live.
Why everyone's profile looks the same
The usual move is embedding github-readme-stats, streak counters and trophy widgets. Three problems. They are someone else's servers, so they rate-limit and go down. They look identical on every profile that uses them. And you learn nothing building them.
The self-hosted way is three files committed straight to the repo:
- Your meme as the portrait. Mine is my classic hackerman gif. A committed gif animates on GitHub forever, no service needed, and it has 10x more personality than ASCII art.
- A neofetch style info card as a hand-built SVG, your OS, stack, socials, with terminal styling and a blinking cursor.
- Your real contribution heatmap as an animated SVG, rendered from data you scraped yourself.
One sizing trap before you lay these out side by side: the profile README column is only about 790px wide. Keep your two image widths summing under about 760px, otherwise GitHub silently max-width shrinks whichever one does not fit and your two columns stop matching in height.
Steal your own stats (no token, no API)
GitHub serves every user's contribution calendar as plain public HTML at github.com/users/YOUR_USERNAME/contributions. No login, no API key, no rate-limit drama for a once-a-day read of your own page.
A ~60 line Python script with requests and BeautifulSoup grabs it, reads every td.ContributionCalendar-day cell (the date and intensity level live right in the attributes, the per-day count sits in a matching tool-tip element), and writes one contributions.json with all 370 days plus your yearly total. A second script turns that JSON into a terminal-styled SVG, rounded cells, GitHub's green palette, month labels, legend, and your total typed at the bottom.
The gotcha that kills every other tutorial
Here is the part nobody tells you, and it cost me two live debugging sessions on my own profile. GitHub does not serve your README images directly, it proxies them through its camo CDN in a sandboxed context. Inside that sandbox, CSS keyframe animations in SVGs never start. Your art works perfectly on localhost, then renders frozen and invisible on your actual profile, every element stuck at its starting opacity of 0. This is why half the animated-profile tutorials on Google produce blank boxes 🤯
Three rules fix it for good:
- Animate with SMIL, the animate and animateTransform tags that live inside the SVG itself. Those run fine behind camo.
- Make the static file the finished state. Every element's plain attributes are its final visible look, and reveals hide it only through the running animation (a values 0;0;1 keyTimes trick). If animations are ever blocked, your graph degrades to a fully rendered static image instead of a blank panel.
- Never begin a reveal at exactly 0 seconds. Browsers sometimes reuse a cached SVG image whose animation clock is paused at t=0, and any animation active at exactly 0 freezes on its hidden first value, blank panel again. Give every reveal begin="0.01s" and the paused case falls back to your visible static art.
The animation stack that makes it feel alive
Static reveals are fine, loops are what make a profile feel alive (and what make it usable as b-roll). The build runs six layers:
- A matrix rain background across both panels, columns of dim green glyphs falling forever behind everything. Make the fall STEPPED (discrete 16px jumps on a shared clock) instead of smooth: that is how the rain in the film actually moves, and it is also the difference between an idle page and a pegged CPU core, because dozens of smoothly-translating columns force the image to repaint every single frame.
- Pop-flash cells. Each cell scales in from nothing with an overshoot bounce while flashing bright green, sweeping left to right across the year in about 2.5 seconds.
- Star glints. A deterministic subset of cells twinkles with little white sparks forever, short flash, long hold, so the raster stays mostly idle.
- A green scan beam that rides the reveal, then ghost-sweeps the whole grid on a 9 second loop.
- Shimmering hot cells. Bright days pulse on staggered infinite timers.
- The typed counter. Your yearly total types itself out behind an animated clip path, then a block cursor blinks next to it forever.
The blinking cursor doubles as your proof of life: its static state is invisible, so if you can see it blinking on your live profile, the animation engine is running through the proxy.
Make it update itself forever
A GitHub Action re-scrapes your calendar every morning, re-renders the SVG, and commits only when something changed. The [skip ci] in the commit message stops it from re-triggering itself. Free on public repos.
name: Update profile art
"on":
schedule:
- cron: "17 6 * * *"
workflow_dispatch: {}
push:
branches: [main]
permissions:
contents: write
jobs:
heatmap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -r scripts/requirements.txt
- run: python scripts/fetch_contributions.py
- run: python scripts/render_heatmap_svg.py
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: refresh contribution graph [skip ci]"
file_pattern: "data/contributions.json contrib-heatmap.svg"Or paste this one prompt into Claude Code
Everything above, the scripts, the SVGs, the workflow, both animation traps, is one prompt for Claude Code. Swap in your username and your own gif, it builds and pushes the whole thing, then verifies the animation actually plays on your live profile. Same energy as the living website build, you describe the system, the agent ships it.
Build me an animated GitHub profile README the self-hosted way. No third party widgets, no tokens.
1. Create (or clone) my profile repo <username>/<username> and replace its README.
2. Layout, centered: my contribution heatmap SVG on top, then a table with my favorite meme gif on the left (I will give you the file) and a neofetch style info card SVG on the right, then one line of social links. Keep the gif and card display widths summing under about 760px, the profile README column is only about 790px wide and it silently max-width shrinks whichever image does not fit, which breaks the height alignment.
3. Write scripts/fetch_contributions.py: download https://github.com/users/<username>/contributions (public HTML, no token needed), parse every td.ContributionCalendar-day cell (data-date, data-level, and the per day count from its matching tool-tip element), save data/contributions.json including the yearly total.
4. Write scripts/render_heatmap_svg.py: render that JSON as a dark terminal style SVG, rounded cells, GitHub green palette, month and weekday labels, legend, total line, fake macOS titlebar dots.
5. Write scripts/make_info_card.py: a neofetch style info card SVG with my OS, stack, what I ship, and my socials, ending in a prompt line with a blinking block cursor. Match its exact pixel height to the gif at its display width so the two columns align.
6. CRITICAL, this is where every tutorial fails: animate with SMIL only (animate and animateTransform tags). CSS keyframe animations inside SVGs do NOT run behind GitHub's camo image proxy, the art freezes invisible. Every element's static attributes must be its finished visible state, reveals hide elements only through a running animation (values="0;0;1" with keyTimes), and every reveal must use begin="0.01s" instead of 0, because a cached image can hand you a SMIL timeline paused at t=0 and anything active at exactly 0 freezes on its hidden first value. Built this way the art degrades to fully visible static, never blank. Give it: a full-panel matrix rain background of falling glyphs clipped to each panel (STEPPED discrete jumps on a shared clock, film-authentic AND cheap, smoothly translating dozens of columns will peg a CPU core repainting every frame), cells that pop in with scale overshoot plus a bright green flash as the wave sweeps left to right, star glints twinkling forever on a deterministic subset of cells (short flash, long hold), a green scan beam that ghost sweeps the grid on a 9s loop, bright cells that shimmer on staggered infinite timers, and the yearly total typing itself out next to a blinking cursor. Rain characters must be XML-safe (no < > & inside the SVG text).
7. Add .github/workflows/update-profile-art.yml: daily cron plus workflow_dispatch plus push trigger, pip install requests and beautifulsoup4, run the fetch and render scripts, auto commit with [skip ci] in the message.
8. Push, watch the Action succeed, then screenshot my live profile and confirm the animation actually plays (the blinking cursor being visible is the proof, its static state is invisible). Reload a few times to hit the cached-image path too.Get the next one first
New prompts every week.
Free. The new drops and the tools behind them, before they hit the feed.
No spam · New issues Sunday · Unsubscribe anytime
Need it custom?
Want this built for you?
Tell me the idea and I’ll build it. An app, a tool, an automation. You don’t need to be technical.
Related guides
Guide
Keep 4, Cut 4: The AI Coding Tools I Actually Ship With
Copilot did not make the cut. The full keep-or-cut scoreboard, why each tool lives or dies, and the eight-tool stack I actually ship with every day.
Guide
Clone Any App With AI (And Outrank It On Google)
Give an AI a URL and it rebuilds the whole app as your own, then builds the pages to outrank the original on Google.
Guide
Build a Viral Peekaboo Login Page With AI (Replit, No Code)
The cute login form where the characters cover their eyes when you type your password. Here are the exact prompts to build it in one shot.
Guide
Build a Living Website With One Prompt (Claude Fable 5 x Higgsfield MCP)
The full system from the video: one prompt inside Claude generates a cinematic character with Higgsfield, animates it, then codes a website where it watches your visitor's cursor with its eyes.
Frequently asked questions
No. The last section's prompt makes Claude Code write the scripts, the SVGs and the workflow for you. If you can create a repo and paste a prompt, you can ship this. If you want to go deeper after, start with the coding tools I actually ship with.
You are reading the same public HTML page GitHub shows any logged-out visitor of your profile, once per day, for your own account. That is about as gentle as automation gets. Keep the daily cron (do not hammer it every minute) and you are being a good citizen.
They work, but they are third-party servers that rate-limit, occasionally die, and look identical on every profile. This build has zero external dependencies at view time, every file is yours, and the animation style is something the widget services literally cannot do.
SMIL animations play in every modern browser, including through GitHub's image proxy, which is exactly why this build uses SMIL instead of CSS keyframes. Anywhere animations are paused or blocked, the design degrades to a fully rendered static graph instead of a blank box, that fallback is built into how every element is authored.
No. Public repo, free GitHub Actions minutes (a run takes about 15 seconds a day), no API tokens, no paid services anywhere in the chain.
