Full-year light/dark GIFs: a character moves through a GitHub-like contribution grid. They illustrate a dated real-data snapshot; GitHub's native graph is untouched.
The cat uses a fixed 365-day shin4141 snapshot from 2026-09-20. Its 53-week grid has one cell per recorded day, plus only the blank partial-week padding needed for a Sunday–Saturday calendar. Real captures include GitHub's relative contribution level for each date, so the default green shades follow the native graph while preserving the actual counts. The one-image fox alternative uses synthetic counts and a custom warm palette; dark version.
Start with the profile starter: copy its workflow, JSON, one icon and README snippet; change your GitHub username; enable Actions and run once. Customize the icon and colors whenever you like. Successful daily/manual runs publish both theme GIFs and cache-busting links. A failed run keeps the previous images. No extra secret is needed for public contribution data.
Python 3.10+ is required. Clone this repository, then run the following from its root:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python render.py --config cat.json --activity shin_activity_2026-09-20.json --seed 216 --theme light --out my-profile.gif
python render.py --config cat.json --activity shin_activity_2026-09-20.json --seed 216 --theme dark --out my-profile-dark.gifThese are your first light/dark copies of the crowned-cat example. For your own version:
-
Save your icon as
your-icon.pngbeside your JSON config (PNG, JPEG, or another Pillow-readable image; a transparent square icon looks best). Copyconfig.example.jsontomy-config.json; replaceYOUR_LOGINwith your GitHub username, and changeicon,accent, andglowto your image filename and#RRGGBBcolors. Paths are relative to the config file. Optionalaccent_dark/glow_darkoverride only the dark version. -
Make a clearly synthetic test snapshot for that username, then render it:
python render.py --sample my-activity.json --username YOUR_LOGIN python render.py --config my-config.json --activity my-activity.json --out my-profile.gif python render.py --config my-config.json --activity my-activity.json --theme dark --out my-profile-dark.gif
Only one icon image is necessary: it bobs, tilts, rests and wakes in both scenes. The cat's sleep_icon and wake_icon are optional extra poses; omit both keys for a single-image character, as in fox.json. No custom animation frames or drawing program are needed.
For your real public contribution data, install the GitHub CLI and authenticate with gh auth login on your own machine. gh auth status should succeed; gh api graphql requires an authenticated token permitted to view that user's contribution calendar. The renderer only reads user(login: ...) { contributionsCollection { contributionCalendar { ... } } } and needs no GitHub write operation. GitHub CLI's login defaults include broader repo, read:org, and gist OAuth scopes; those are CLI defaults, not extra powers requested by this script. GitHub documents optional read:user for including private/internal contributions; it is not needed for the public example. Do not paste tokens into config files, commits, or issues. Run:
python render.py --capture my-activity.json --username YOUR_LOGIN
python render.py --config my-config.json --activity my-activity.json --out my-profile.gif
python render.py --config my-config.json --activity my-activity.json --theme dark --out my-profile-dark.gifCapture defaults to the last 365 UTC calendar days; use --from-date YYYY-MM-DD --to-date YYYY-MM-DD to set the range explicitly. Rendering requires 365 consecutive dated entries and never turns an absent date into a zero-contribution claim. Re-run these two commands whenever you want a new static snapshot/GIF, then commit the new GIF to the repository that serves your profile. The capture JSON includes observed_at, counts, and GitHub's relative color levels; review it before publishing. Public counts can differ from what you expect because of GitHub's contribution and privacy rules. --sample is synthetic and never queries GitHub; --capture is the authenticated real-data path. These local commands do not schedule updates; the starter workflow does.
Commit both GIFs to your special YOUR_LOGIN/YOUR_LOGIN profile repository and put this in its README.md:
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./my-profile-dark.gif">
<img src="./my-profile.gif" alt="My decorative activity animation">
</picture>If the GIF stays in this repository instead, reference an immutable commit, for example https://raw.githubusercontent.com/OWNER/github-profile-motion/COMMIT/my-profile.gif. A moving title is optional: python render.py --heading-out heading.gif --heading-mobile-out heading_mobile.gif, then use a <picture> element like the one above. The sample heading text is fixed in render.py; edit HEADING if your profile needs different words. Keep meaningful alt text: a reduced-motion client may show only frame one.
render.pymakes deterministic, script-free light/dark GIFs from an icon, palette, seed, and 365-day dated JSON. The two-sceneshockloop is the default.--pattern touchis a retained alternate, not another required sprite set.cat.jsonpluscrowned_cat*.pngare the ready-to-run character;fox.jsonandfox.pngdemonstrate one-image substitution.make_icons.pyregenerates these original small icons.shin_activity_2026-09-20.jsonis a frozen public GitHub GraphQL snapshot;example_activity.jsonis explicitly synthetic. Each activity file contains ausernameanddaysentries withdateand nonnegative integercount; the username must match the config. Rendering does not modify input counts.test_render.pychecks stable frames, two-scene continuity, input preservation, and alternate-icon output. Runpython -m unittest -v test_renderafter installing dependencies.update_profile.pyand its tests prepare validated real-data GIFs for both themes plus dated README links; the reusable workflow commits all three files in the caller's profile repository. A failure produces a failed Actions run, not a silent synthetic image.
The daily refresh rollout record describes impact and rollback without changing the fixed V216 examples.
Code and original included artwork are available under MIT. The renderer, config, icons, examples, and tests were extracted from Decision-OS V13 LoopKit at a3c3e6633b13684adc08beab28883a57b11d5cbb; that repository holds the design/decision history and Aspire entry. This repository is the canonical place for future generator changes. See the live example on Shin's profile.