{"id":15576440,"url":"https://github.com/njsmith/animation-experiments","last_synced_at":"2025-07-19T15:41:11.484Z","repository":{"id":145901620,"uuid":"189786398","full_name":"njsmith/animation-experiments","owner":"njsmith","description":null,"archived":false,"fork":false,"pushed_at":"2019-06-27T08:20:12.000Z","size":154,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-19T09:41:52.092Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/njsmith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-01T23:29:07.000Z","updated_at":"2019-06-27T08:20:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"caa44ca2-cf2a-42b3-b694-beb26d9691a4","html_url":"https://github.com/njsmith/animation-experiments","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/njsmith%2Fanimation-experiments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/njsmith%2Fanimation-experiments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/njsmith%2Fanimation-experiments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/njsmith%2Fanimation-experiments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/njsmith","download_url":"https://codeload.github.com/njsmith/animation-experiments/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243256541,"owners_count":20262167,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T18:49:20.788Z","updated_at":"2025-03-12T16:40:44.753Z","avatar_url":"https://github.com/njsmith.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# What is this\n\nThis is a sweet and hacky little framework for generating vector\nanimations, especially diagrams for the Trio docs.\n\nThe basic flow is:\n\n- Make a template SVG file. I recommend Inkscape (one of my favorite\n  programs of all time).\n- Give some of the objects in your template SVG file unique ids.\n- Write a Python script describing what you want those objects to do\n  and when, using the framework in `animhelpers.py`\n- Run the script, which will read in the template, mangle it\n  appropriately, and write it out to a new animated SVG file.\n  \nSVG is just XML, so this isn't actually that complicated. For the\nanimation part we use\n[SMIL](https://en.wikipedia.org/wiki/Synchronized_Multimedia_Integration_Language)\ntags, which are well-supported in all popular browsers *except*\nInternet Explorer. Sorry. Maybe we can polyfill or something? Or\nsomehow record the animations to GIFs as a fallback? I haven't figured\nthat out. Hopefully most IE users at least have Firefox or something\navailable too as an option?\n\n\n# Requirements\n\nTo run the scripts you need:\n\n- Inkscape on your path (used to automatically convert text to paths)\n- Fonts (where Inkscape can find them):\n  - Montserrat\n  - DejaVu Sans Mono\n- `pip install lxml tinycss2 svgpathtools scour`\n\n\n# Making animations\n\n## General info\n\nLook at some existing .py files and template .svgs to get a sense of\nhow things fit together.\n\nThe wacky animation logic guts are all in `animhelpers.py`.\n\nIt might also help to be generally familiar with how SMIL works. [This\nis a good\noverview](https://css-tricks.com/guide-svg-animations-smil/), and\nhere's the [MDN entry\npoint](https://developer.mozilla.org/en-US/docs/Web/SVG/SVG_animation_with_SMIL).\n\nAfter generating an animation, there's a `whatever.svg.tmp` file left\nbehind, which is the un-minimized version. It might be a bit easier to\nread if you need to look at it. It's not particularly mysterious.\n\nThe final output is pretty readable too honestly. We use `scour` for\nSVG minimization. (This runs automatically as part of your script, you\ndon't have to do anything special.) In my tests it produces files that\nare ~5% bigger than fancier alternatives, but the fancier alternatives\nI tried were all so fancy that they mangled the animation tags and\nbroke everything. Plus `scour` is on PyPI, which is convenient.\n\n\n## The timeline\n\nThe Python helpers use globals and stuff everywhere. Everything is\nstateful.\n\nPart of that state is the \"current time\". When you add an animation by\ncalling `animate` or `slide`, then the animation's start point is\nautomatically set to the current time, and then the current time is\nupdated to point to the end point of that animation. (This is usually\n`dur` seconds after the start, but if you get fancy with repeats or\nsomething it might not be.)\n\nIf you don't want the time to change, use `with keep_time(): ...`.\n\nYou can also change the current time explicitly to an absolute time\nwith `set_time`, or a relative time using `wait`. If you're into\ntime-travel, you can pass negative seconds to `wait` (since it's\nreally moving around on the animation timeline, not real time).\n\nThere's also some clever machinery where `animate` and `slide` return\nan object that represents the start/end time of the animation, and you\ncan use it to access related times:\n\n```python\nanim1 = animate(...)\nset_time(anim1.start + 3)\n# This will start 3 seconds after anim1 started\nanimate(...)\n```\n\nI'm not sure if this is actually useful though.\n\n\n## Animation controls\n\nOne thing `animhelpers.py` does is inject JS-based controls to play,\npause, replay the animation. These are in `player.js`,\n`play-overlay.svg`, `replay-overlay.svg`.\n\nThere's a gross hack that you might need to be aware of. It's\nsurprisingly hard to figure out when a sequence of SMIL animations\nhave finished. So the JS code currently assumes that whichever\nanimation appears last *in the .svg source code*, is also the one that\nfinishes last in the animation timeline. (Which in turn will be\nwhichever call to `animate` or `slide` appears last in your Python\nscript.) This is almost always going to be true just because it would\nbe weird to do it otherwise, but watch out in case you're doing\nsomething weird.\n\n\n## `LineSeq`\n\nThe `LineSeq` helper is convenient for managing the background\nhighlights on source code.\n\nIt's pretty simple-minded. To use:\n\n- In Inkscape, draw all the backgrounds as you like.\n- Give them ids like `COMMONPREFIX-0`, `COMMONPREFIX-1`, ...\n- In your `.py` file, do `lines = LineSeq(\"COMMONPREFIX\")`.\n- That will make sure that only `COMMONPREFIX-0` is visible at the\n  start, and each time you call `lines.next()` it will animate a\n  transition to the next object.\n\n\n## Misc tips and tricks\n\nIn Inkscape, you can see or edit an object's `id` by using the \"Object\nProperties\" pane. The \"XML editor\" pane is also useful sometimes to\nsee what's going on. (Internally, Inkscape is basically a very very\nvery specialized text editor: it keeps the SVG internally and that\n*is* its internal source-of-truth about things. You can make arbitrary\nedits through the XML editor and Inkscape's display will update live!)\n\nYou can also edit the template .svg directly in your text editor, but\nInkscape won't automatically reload if the file is changed on disk, so\nif you do this then make sure to exit Inkscape first, or use File -\u003e\nRevert.\n\nWhen you use `slide` it applies a *relative* motion to the object. So\nit doesn't matter where you drew the path in inkscape. It's convenient\nto attach it to some reference point of the object, but you don't have\nto. If you want several objects to move in parallel while keeping\ntheir relative position, you can draw one path and them have them all\nfollow it (possibly at different times).\n\n\n# Rejected alternatives\n\nThere are a few different ways to show animations on the web.\n\nYou can use CSS animation. This is what the standards bodies are\ngradually moving towards. But at least at the time of writing, [there\nisn't any portable way to move an object along a\npath](https://caniuse.com/#feat=css-motion-paths), which is pretty\ncrucial for us. So this is out for now.\n\nYou can make your animations in Adobe After Effects, and then export\nthem to \"Bodymovin JSON\", and play them with\n[Lottie](http://airbnb.io/lottie/). AFAICT this is currently the\nindustry standard for high-end animations. But:\n\n- Adobe After Effects is expensive and has a steep learning curve\n- You have to do everything with clicking and dragging; you can't\n  describe an animation using a `for` loop. (Or maybe you can somehow,\n  if you get far enough up that learning curve, but probably anyone\n  writing Trio docs already knows Python.)\n\nYou can use the open-source [Synfig Studio](https://www.synfig.org/).\nThis is pretty cool actually. But its vector editing tools are way\nclunkier than Inkscape's, so you'd still need to use Inkscape to build\nmost of the diagram, and then add motion in Synfig Studio. And it's\nnot scriptable. And it only outputs raster-based video formats.\n(Though there's a [GSoC project to add Lottie/Bodymovin as an export\nformat](https://forums.synfig.org/t/gsoc-2019-export-animation-for-web/9507/45).)\n\nYou can build something with a JS animation library. But there are\nlike dozens of these, I can't tell which ones are actually\nused/maintained, they mostly seem to work at a lower-level of\nabstraction than SMIL, and I don't actually know JS.\n\nSo that's why for now we're using SMIL, even if it doesn't work in IE\nand CSS animations are The Way Of The Future™.\n\nHopefully if we have to eventually convert to another delivery format,\nwe can do that by just rewriting the `animate`, `slide`, etc. helpers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnjsmith%2Fanimation-experiments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnjsmith%2Fanimation-experiments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnjsmith%2Fanimation-experiments/lists"}