<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pixel Art on Christian Martínez De La Rosa</title><link>https://christt105.github.io/tags/pixel-art/</link><description>Recent content in Pixel Art on Christian Martínez De La Rosa</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 21 Aug 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://christt105.github.io/tags/pixel-art/index.xml" rel="self" type="application/rss+xml"/><item><title>How a GIF for My GitHub Profile Turned Into the Parallax Scene Editor</title><link>https://christt105.github.io/blog/github-profile-readme/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0200</pubDate><guid>https://christt105.github.io/blog/github-profile-readme/</guid><enclosure url="https://christt105.github.io/blog/github-profile-readme/github-profile-screenshot.png" length="343324" type="image/png"/><description>&lt;p&gt;Welcome to a new post!&lt;/p&gt;&#10;&lt;p&gt;This time it&amp;rsquo;s about something small: the README on my GitHub profile. I wanted it to be simple, a GIF that says something about me, a bit nerdy but not overdone, a short description and little else. It ended up taking a lot longer than it had any right to.&lt;/p&gt;&#10;&lt;h2 id="the-idea"&gt;The idea&#10;&lt;/h2&gt;&lt;p&gt;The starting point was Pokémon Emerald&amp;rsquo;s intro: the character rides a bike while a handful of Pokémon run alongside, right before the credits. I&amp;rsquo;ve always liked that scene, so I wanted something in the same spirit, personalized, but recognizably the same idea. Simple in theory.&lt;/p&gt;&#10;&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="386" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/PokemonEmeraldIntro.jpg" width="686"&gt;&lt;/p&gt;&#10;&lt;h2 id="first-attempt-aseprite"&gt;First attempt: Aseprite&#10;&lt;/h2&gt;&lt;p&gt;I downloaded &lt;a class="link" href="https://www.aseprite.org/" target="_blank" rel="noopener"&#10; &gt;Aseprite&lt;/a&gt; because I&amp;rsquo;d wanted an excuse to learn it for a while. While I got familiar with it, I asked Claude to rip the sprites out of &lt;a class="link" href="https://github.com/pret/pokeemerald" target="_blank" rel="noopener"&#10; &gt;pokeemerald&lt;/a&gt;, the decompilation project. I didn&amp;rsquo;t have much faith it would go anywhere, but it did it well: it pulled the sprites out reasonably organized, in pieces, then stitched them back into several spritesheets, and did the same for the backgrounds of the different scenes, urban, sea and forest.&lt;/p&gt;&#10;&lt;p&gt;I asked if it could rebuild the original scene in Aseprite, and it managed that too. That&amp;rsquo;s when I hit the actual problem: Aseprite works frame by frame. There&amp;rsquo;s no way, as far as I know, to move something freely between arbitrary keyframes and have it interpolate; you draw or place each frame by hand. Fine for spriting, not for choreographing a camera and actors over hundreds of frames.&lt;/p&gt;&#10;&lt;h2 id="plot-twist"&gt;Plot twist&#10;&lt;/h2&gt;&lt;p&gt;So I asked for the same scene to be built directly from a JSON with the actors&amp;rsquo; data and positions, taken straight from the game. It matched the original almost exactly. And that&amp;rsquo;s when the idea I actually wanted showed up: what if I turned an afternoon project into a week-long one?&lt;/p&gt;&#10;&lt;p&gt;That&amp;rsquo;s how the &lt;a class="link" href="https://github.com/christt105/parallax-scene-editor" target="_blank" rel="noopener"&#10; &gt;Parallax Scene Editor&lt;/a&gt; was born. I asked for a web page that would work with sprites and a JSON describing the scene, and we iterated from there. Two constraints from the start: everything local, and it had to write directly to disk, not to some app-only state you export by hand at the end.&lt;/p&gt;&#10;&lt;h2 id="the-editor"&gt;The editor&#10;&lt;/h2&gt;&lt;h3 id="parallax"&gt;Parallax&#10;&lt;/h3&gt;&lt;p&gt;Before going further, a quick aside for anyone unfamiliar with the term. Parallax is the age-old trick for faking depth in 2D. Instead of a single background, you stack several layers (sky, mountains, trees, ground&amp;hellip;) and each one moves at its own speed, the far ones slower, the near ones faster. The eye reads that difference in speed as distance, even though underneath it&amp;rsquo;s just flat sprites. It&amp;rsquo;s the same technique 16-bit games used, and the one Pokémon Emerald uses in its intro.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="Example of parallax scrolling with several layers moving at different speeds" class="gallery-image" data-flex-basis="411px" data-flex-grow="171" height="448" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://images.squarespace-cdn.com/content/551a19f8e4b0e8322a93850a/1573861732601-PTWHSU2HW5BZ9C2IASCM/Intro_Parallax.gif?content-type=image%2Fgif" width="768"&gt;&#10;&lt;em&gt;Example from &lt;a class="link" href="https://www.slynyrd.com/blog/2019/11/12/pixelblog-23-parallax-scrolling" target="_blank" rel="noopener"&#10; &gt;slynyrd.com&lt;/a&gt;, which has a whole post well worth reading on drawing parallax scrolling in pixel art.&lt;/em&gt;&lt;/p&gt;&#10;&lt;h3 id="using-the-editor"&gt;Using the editor&#10;&lt;/h3&gt;&lt;p&gt;As always, here&amp;rsquo;s the editor&amp;rsquo;s repo with the source code for anyone who wants to poke around.&lt;/p&gt;&#10;&lt;a class="github-repo-card" href="https://github.com/christt105/parallax-scene-editor" target="_blank" rel="noopener"&gt;&#10; &lt;img src="https://avatars.githubusercontent.com/u/36189169?v=4" alt="christt105 avatar" class="repo-avatar"&gt;&#10; &lt;h3&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-github" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"&gt;&#10; &lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&#10; &lt;path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" /&gt;&#10;&lt;/svg&gt;&#10;&#10;&#10; christt105/parallax-scene-editor&lt;/h3&gt;&#10; &lt;p&gt;Browser-based editor for seamlessly looping pixel art parallax scenes. Serverless, zero uploads.&lt;/p&gt;&#10; &lt;div class="repo-stats"&gt;&#10; &lt;span&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-star"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M8.243 7.34l-6.38 .925l-.113 .023a1 1 0 0 0 -.44 1.684l4.622 4.499l-1.09 6.355l-.013 .11a1 1 0 0 0 1.464 .944l5.706 -3l5.693 3l.1 .046a1 1 0 0 0 1.352 -1.1l-1.091 -6.355l4.624 -4.5l.078 -.085a1 1 0 0 0 -.633 -1.62l-6.38 -.926l-2.852 -5.78a1 1 0 0 0 -1.794 0l-2.853 5.78z" /&gt;&lt;/svg&gt; 1&lt;/span&gt;&#10; &lt;span&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-git-fork"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M10 18a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /&gt;&lt;path d="M5 6a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /&gt;&lt;path d="M15 6a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /&gt;&lt;path d="M7 8v2a2 2 0 0 0 2 2h6a2 2 0 0 0 2 -2v-2" /&gt;&lt;path d="M12 12l0 4" /&gt;&lt;/svg&gt; 0&lt;/span&gt;&#10; &lt;span&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-calendar-event"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" /&gt;&lt;path d="M16 3l0 4" /&gt;&lt;path d="M8 3l0 4" /&gt;&lt;path d="M4 11l16 0" /&gt;&lt;path d="M8 15h2v2h-2l0 -2" /&gt;&lt;/svg&gt; 2026-08-29&lt;/span&gt;&#10; &lt;/div&gt;&#10; &lt;/a&gt;&#10;&lt;p&gt;Let&amp;rsquo;s see how this little tool works.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="The editor open on the demo scene, with layers, actors and the timeline visible" class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="1080" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/parallax-editor-loop.gif" srcset="https://christt105.github.io/blog/github-profile-readme/parallax-editor-loop_hu_7eb43a54268906fc.gif 800w, https://christt105.github.io/blog/github-profile-readme/parallax-editor-loop_hu_c20eaedc1aaff01c.gif 1600w, https://christt105.github.io/blog/github-profile-readme/parallax-editor-loop.gif 1920w" width="1920"&gt;&lt;/p&gt;&#10;&lt;p&gt;A parallax scene here is one JSON file: camera, layers, actors, keyframes. You edit it while it plays on a loop, and it tells you in red the moment something won&amp;rsquo;t close cleanly. Some of what it ended up doing:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Reads and writes straight to your disk.&lt;/strong&gt; You open a folder with the File System Access API (Chromium only) and from then on every change is written back to the same file a few hundred milliseconds after you stop touching it. A pill next to &lt;em&gt;Save&lt;/em&gt; always says where it&amp;rsquo;s writing.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Sprites reload themselves.&lt;/strong&gt; Repaint something in Aseprite, hit save, and a couple of seconds later it&amp;rsquo;s already on the canvas, with no action from you.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Keyframed actors with easing&lt;/strong&gt;, plus &lt;code&gt;sine&lt;/code&gt;/&lt;code&gt;cosine&lt;/code&gt;/&lt;code&gt;wobble&lt;/code&gt; motion stacked on top for the little touches, a bounce on the bike, a bird bobbing mid-flight, without hand-drawing it.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;It does the loop-closing arithmetic for you.&lt;/strong&gt; If a layer&amp;rsquo;s speed won&amp;rsquo;t tile cleanly, or an actor&amp;rsquo;s cel count won&amp;rsquo;t divide evenly into the loop, the editor doesn&amp;rsquo;t just flag it, it offers the exact fix: the two nearest speeds that do close, or a new loop length, with a button for each.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Onion skin, a draggable timeline, undo history&lt;/strong&gt;, the usual things you&amp;rsquo;d want and miss if they weren&amp;rsquo;t there.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Export to GIF, WebM or a PNG sequence.&lt;/strong&gt; The GIF encoder is written from scratch. If the whole animation fits in 256 colors, which most pixel art does, the palette is exact.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Here&amp;rsquo;s what it looks like already at work on the real project, with the rider, Mudkip and a flying Tropius, and the panel on the right flagging in red that Tropius&amp;rsquo;s cycle doesn&amp;rsquo;t close the loop:&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="The editor with the Pokémon Emerald project loaded, showing the rider, Mudkip, a flying Tropius and the loop-closing warning" class="gallery-image" data-flex-basis="458px" data-flex-grow="190" height="1320" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/editor-pokemon-project.png" srcset="https://christt105.github.io/blog/github-profile-readme/editor-pokemon-project_hu_10e6b17cdc9ee60.png 800w, https://christt105.github.io/blog/github-profile-readme/editor-pokemon-project_hu_7dc6a8891bcae378.png 1600w, https://christt105.github.io/blog/github-profile-readme/editor-pokemon-project_hu_5c27df083c3491d.png 2400w, https://christt105.github.io/blog/github-profile-readme/editor-pokemon-project.png 2520w" width="2520"&gt;&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s fully open source, no dependencies, no build step, so GitHub Pages serves it as-is. There&amp;rsquo;s a live version at &lt;a class="link" href="https://christt105.github.io/parallax-scene-editor/" target="_blank" rel="noopener"&#10; &gt;christt105.github.io/parallax-scene-editor&lt;/a&gt; if you want to poke at it.&lt;/p&gt;&#10;&lt;h2 id="making-the-actual-gif"&gt;Making the actual GIF&#10;&lt;/h2&gt;&lt;p&gt;With the editor in a good enough state, I got back to the GIF itself. I kept the character on the bike, positioned it in the scene, and went looking for a running Pokémon to put next to it. I found a Mudkip spritesheet from Pokémon Mystery Dungeon that, surprisingly, matched the size and perspective I needed almost out of the box. Coincidentally, it&amp;rsquo;s my favorite Pokémon.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="A Mudkip plush from the Pokémon Center in Japan that a friend who knows me very well got me" class="gallery-image" data-flex-basis="320px" data-flex-grow="133" height="1500" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/IMG_20260820_224501.jpg" srcset="https://christt105.github.io/blog/github-profile-readme/IMG_20260820_224501_hu_24bc84126df976ae.jpg 800w, https://christt105.github.io/blog/github-profile-readme/IMG_20260820_224501_hu_fa34aa666f9a4752.jpg 1600w, https://christt105.github.io/blog/github-profile-readme/IMG_20260820_224501.jpg 2000w" width="2000"&gt;&lt;/p&gt;&#10;&lt;p&gt;The one problem was the color. Mystery Dungeon&amp;rsquo;s art style is different enough from Emerald&amp;rsquo;s that Mudkip stuck out next to everything else. I asked Claude to look at the other Pokémon sprites already in the scene and adjust Mudkip&amp;rsquo;s palette to match, and the result isn&amp;rsquo;t bad, mostly around the outline.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="The Mudkip spritesheet straight out of Pokémon Mystery Dungeon, untouched" class="gallery-image" data-flex-basis="720px" data-flex-grow="300" height="132" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/mudkip-original.png" width="396"&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="The Mudkip spritesheet recolored and adjusted, as used in the project" class="gallery-image" data-flex-basis="720px" data-flex-grow="300" height="132" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/mudkip-project.png" width="396"&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="The Torchic spritesheet used in Pokémon Emerald" class="gallery-image" data-flex-basis="720px" data-flex-grow="300" height="144" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/torchic-project.png" width="432"&gt;&lt;/p&gt;&#10;&lt;p&gt;I also tried some custom Pokémon animations I found online, but the style and perspective were too far off to blend in, so I dropped them and kept it to the rider and Mudkip running through a forest background. It would have been fun to put together a full team of six I&amp;rsquo;ve actually played with at some point, but getting sprites to that level of consistency is more than I want to take on right now. I applied a slight zoom-out and left it there, simple, but clean.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="The final result: the bike rider and a running Mudkip looping over a forest background" class="gallery-image" data-flex-basis="640px" data-flex-grow="266" height="480" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/bike-ride.gif" srcset="https://christt105.github.io/blog/github-profile-readme/bike-ride_hu_cbb9caeee52a9e85.gif 800w, https://christt105.github.io/blog/github-profile-readme/bike-ride.gif 1280w" width="1280"&gt;&lt;/p&gt;&#10;&lt;h2 id="automating-the-update"&gt;Automating the update&#10;&lt;/h2&gt;&lt;p&gt;The editor exports to GIF and WebM, so the plan was: the Pokémon project repo exports the GIF, and the profile README references it. I wanted that to be as hands-off as possible, so I added a &lt;a class="link" href="https://github.com/christt105/PokemonEmeraldIntroVideo/blob/main/.github/workflows/export.yml" target="_blank" rel="noopener"&#10; &gt;GitHub Action&lt;/a&gt; to the &lt;a class="link" href="https://github.com/christt105/PokemonEmeraldIntroVideo" target="_blank" rel="noopener"&#10; &gt;PokemonEmeraldIntroVideo&lt;/a&gt; repo that runs on every push: it checks out a build of the editor, drives it headlessly through Playwright, and commits the rendered GIF and WebM straight back to &lt;code&gt;out/&lt;/code&gt;.&lt;/p&gt;&#10;&lt;a class="github-repo-card" href="https://github.com/christt105/PokemonEmeraldIntroVideo" target="_blank" rel="noopener"&gt;&#10; &lt;img src="https://avatars.githubusercontent.com/u/36189169?v=4" alt="christt105 avatar" class="repo-avatar"&gt;&#10; &lt;h3&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-github" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"&gt;&#10; &lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&#10; &lt;path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" /&gt;&#10;&lt;/svg&gt;&#10;&#10;&#10; christt105/PokemonEmeraldIntroVideo&lt;/h3&gt;&#10; &lt;p&gt;&lt;/p&gt;&#10; &lt;div class="repo-stats"&gt;&#10; &lt;span&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-star"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M8.243 7.34l-6.38 .925l-.113 .023a1 1 0 0 0 -.44 1.684l4.622 4.499l-1.09 6.355l-.013 .11a1 1 0 0 0 1.464 .944l5.706 -3l5.693 3l.1 .046a1 1 0 0 0 1.352 -1.1l-1.091 -6.355l4.624 -4.5l.078 -.085a1 1 0 0 0 -.633 -1.62l-6.38 -.926l-2.852 -5.78a1 1 0 0 0 -1.794 0l-2.853 5.78z" /&gt;&lt;/svg&gt; 0&lt;/span&gt;&#10; &lt;span&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-git-fork"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M10 18a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /&gt;&lt;path d="M5 6a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /&gt;&lt;path d="M15 6a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /&gt;&lt;path d="M7 8v2a2 2 0 0 0 2 2h6a2 2 0 0 0 2 -2v-2" /&gt;&lt;path d="M12 12l0 4" /&gt;&lt;/svg&gt; 0&lt;/span&gt;&#10; &lt;span&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-calendar-event"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" /&gt;&lt;path d="M16 3l0 4" /&gt;&lt;path d="M8 3l0 4" /&gt;&lt;path d="M4 11l16 0" /&gt;&lt;path d="M8 15h2v2h-2l0 -2" /&gt;&lt;/svg&gt; 2026-08-16&lt;/span&gt;&#10; &lt;/div&gt;&#10; &lt;/a&gt;&#10;&lt;p&gt;The profile README just points at that file&amp;rsquo;s raw URL. So the moment I tweak a speed or nudge a keyframe in the scene and push, the animation regenerates on its own and the README updates with it, no export-commit-push dance on my end.&lt;/p&gt;&#10;&lt;h2 id="the-result"&gt;The result&#10;&lt;/h2&gt;&lt;p&gt;Description of myself, kept short on purpose, and a small list of the stack I use day to day. That gif sits right at the top as the header, which is really the whole idea: something that says a bit about me before a single word does.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="The GitHub profile in dark mode, with the looping bike-rider gif in the README header" class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="1080" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://christt105.github.io/blog/github-profile-readme/github-profile-loop.gif" srcset="https://christt105.github.io/blog/github-profile-readme/github-profile-loop_hu_7437adac256ce14.gif 800w, https://christt105.github.io/blog/github-profile-readme/github-profile-loop_hu_2d62f19a29bb6d12.gif 1600w, https://christt105.github.io/blog/github-profile-readme/github-profile-loop.gif 1920w" width="1920"&gt;&lt;/p&gt;&#10;&lt;p&gt;The whole thing spiraled a lot further than planned, and it makes me laugh a little that I ended up building a full parallax editor just to get one animated GIF onto a profile page. I never checked whether something similar already existed, but here&amp;rsquo;s mine, open source, so anyone who wants to bend it into something else can. I&amp;rsquo;ve already got another idea for the README that&amp;rsquo;s a bit more ambitious, that one&amp;rsquo;s for another post (life permitting).&lt;/p&gt;&#10;&lt;p&gt;Hope you liked it, and feel free to try the editor if you&amp;rsquo;ve got a parallax scene of your own in mind.&lt;/p&gt;&#10;&lt;p&gt;Until next time!&lt;/p&gt;&#10;</description></item></channel></rss>