{"id":17383888,"url":"https://github.com/mcandre/buttery","last_synced_at":"2026-01-07T23:19:54.855Z","repository":{"id":147617208,"uuid":"618748131","full_name":"mcandre/buttery","owner":"mcandre","description":"a video editor with manual motion smoothing","archived":false,"fork":false,"pushed_at":"2025-04-01T22:00:28.000Z","size":33893,"stargazers_count":8,"open_issues_count":23,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-15T05:51:45.599Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mcandre.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2023-03-25T08:26:03.000Z","updated_at":"2025-04-01T22:00:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"326cd4df-68a1-4267-84dd-1cd1b27e0022","html_url":"https://github.com/mcandre/buttery","commit_stats":{"total_commits":226,"total_committers":4,"mean_commits":56.5,"dds":"0.30530973451327437","last_synced_commit":"b6dfda5fbc16d95811a1faa75a9482e2e783acb1"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcandre%2Fbuttery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcandre%2Fbuttery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcandre%2Fbuttery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcandre%2Fbuttery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mcandre","download_url":"https://codeload.github.com/mcandre/buttery/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249016317,"owners_count":21198832,"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-16T07:44:05.254Z","updated_at":"2026-01-07T23:19:54.835Z","avatar_url":"https://github.com/mcandre.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# buttery: a video editor with manual motion smoothing\n\n[![Donate](https://img.shields.io/badge/GUMROAD-36a9ae?style=flat\u0026logo=gumroad\u0026logoColor=white)](https://mcandre.gumroad.com/)\n\n![examples/homer.buttery.gif](examples/homer.buttery.gif)\n\n![examples/cinnamoroll.buttery.gif](examples/cinnamoroll.buttery.gif)\n\n# ABOUT\n\n`buttery` generates continuous GIF loops.\n\n# EXAMPLES\n\n```console\n$ cd examples\n\n$ buttery homer.gif\n\n$ buttery -transparent -stitch FlipH cinnamoroll.gif\n```\n\nSee `buttery -help` for more options.\n\n# DOCKER HUB\n\nhttps://hub.docker.com/r/n4jm4/buttery\n\n# DOWNLOAD\n\nhttps://github.com/mcandre/buttery/releases\n\n# INSTALL FROM SOURCE\n\n```console\n$ go install github.com/mcandre/buttery/cmd/buttery@latest\n```\n\n# DOCUMENTATION\n\nhttps://pkg.go.dev/github.com/mcandre/buttery\n\n# LICENSE\n\nBSD-2-Clause\n\n# RUNTIME REQUIREMENTS\n\n(None)\n\n# CONTRIBUTING\n\nFor more information on developing buttery itself, see [DEVELOPMENT.md](DEVELOPMENT.md).\n\n# LINT\n\n`buttery -check \u003cGIF\u003e` can act as a linter for basic GIF file format integrity. In the event of a corrupt GIF file, the program emits a brief message and exits non-zero.\n\n# INSPECT\n\n`buttery -getFrames \u003cGIF\u003e` reports the frame count. This is useful for planning edits, particularly towards the far end of the original animation sequence.\n\n# BACKGROUNDS\n\nThe `-transparent` option changes the disposal mode from none to background, and changes the background from black to clear.\n\n# TRANSITIONS\n\n## Mirror\n\nThe `-stitch Mirror` option is the primary loop smoothing transition, and the default transition setting.\n\nMirror twists the GIF timeline around like a Mobius strip, so that it arrives naturally back at the start. This is useful for smoothing GIF's that present misaligned images at the extreme ends of the loop.\n\nWe can diagram logically how Mirror works, by examining its effect on the frame sequence. With the notation:\n\n```text\nreal frame sequence (successive sequence repeated during infinite loop playback...)\n```\n\n### Before\n\n```text\n1 2 3 (1 2 3 ...)\n```\n\nEach restart of the loop has a jarring visual jump from frame 3 to its successor frame 1.\n\n### After\n\n```text\n1 2 3 2 (1 2 3 2 ...)\n```\n\nOf course, running the `buttery` editor yourself is the best way to appreciate how it works.\n\nBy mirroring the sequence backward in time, we remove the biggest visual jump. The overall visual effect is that of a sailor rowing back and forth in place. The Mirror transition often dramatically improves the smoothness of a GIF loop.\n\nHowever, some motion may still appear awkward with mirroring, such as sharp, quick motions towards the extreme ends of the loop, or motions that appear to defy physical entropy. For this reason, we provide alternative transitions and other editing tools, described below.\n\n## FlipH / FlipV\n\nThe transision settings `-stitch FlipH` or `-stitch FlipV` disguise jarring misalignment, by reflecting the frames horizontally or vertically.\n\nWith the notation:\n\n* `R`: An original \"right\" frame\n* `L`: A frame reflected horizontally \"leftward\"\n* `U`: A original \"upright\" frame\n* `D`: A frame reflected vertically \"downward\"\n\n### Before\n\nFlipH:\n\n```text\nR R R (R R R ...)\n```\n\nFlipV:\n\n```text\nU U U (U U U ...)\n```\n\n### After\n\nFlipH:\n\n```text\nR R R L L L (R R R L L L ...)\n```\n\nFlipV:\n\n```text\nU U U D D D (U U U D D D ...)\n```\n\nThe `FlipH`/`FlipV` transitions are snappy, with an effect like rebounding a tennis ball across a net.\n\n## Shuffle\n\nThe `-stitch Shuffle` transition setting randomizes the frame sequence.\n\n### Before\n\n```text\n1 2 3 4 5 6\n```\n\n### After\n\nExample ordering:\n\n```text\n6 3 1 4 2 5\n```\n\nNaturally, the more unique frames available, the more opportunity for unique random orderings.\n\nThis transition tends to artificially accelerate the perceived animation speed.\n\nThis transition hides a single jarring misalignment, in the noise of a completely random, spastic animation.\n\n## PanH / PanV\n\nThe `PanH` / `PanV` transitions offset the canvas at `-panVelocity \u003cn\u003e` pixels per frame.\n\n## Fade\n\nThe transision setting `-stitch Fade` applies fade to black, fade to white, etc. time color gradient effects.\n\n### Before\n\n```text\nno_fade ... no_fade ... no_fade ... no_fade ... no_fade\n```\n\n### After\n\n```text\nmax_fade ... less_fade ... no_fade ... less_fade ... max_fade\n```\n\n`-fadeColor 0xRRGGBB` customizes the fade hue (default: black).\n\n`-fadeRate \u003cv\u003e` adjusts fade velocity (default: 1.0).\n\n## None\n\nThe `-stitch None` transition setting applies no particular transition at all between animation cycles. In art, sometimes less is more.\n\n# SUPERCUTS\n\nAnimation smoothing takes a long time. We recommend pre-cutting your source assets to the desired subsequence. Every frame removed from the input GIF makes the `buttery` editing process faster.\n\nOften, animations appear to accelerate when frame are removed. This is not always a bad thing; sometimes a fast animation helps to smooth over more subtle details.\n\n## Trim Start / End\n\nThe `-trimStart \u003cn\u003e` / `-trimEnd \u003cn\u003e` options drop `n` frames from the start and/or end of the original sequence. Zero indicates no trimming.\n\nFor brevity, we will now assume the None transition and elide the successive sequence repetitions.\n\n### Before\n\n```text\n1 2 3 4 5\n```\n\n### After\n\nWith `-trimStart 1`:\n\n```text\n2 3 4 5\n```\n\nWith `-trimEnd 1`:\n\n```text\n1 2 3 4\n```\n\nWith `-trimStart 1` and `-trimEnd 1`:\n\n```text\n2 3 4\n```\n\n## Trim Edges\n\nFor convenience, we provide a similar option `-trimEdges \u003cn\u003e`. This drops `n` frames from both sides of the original sequence. Zero indicates no trimming.\n\n### Before\n\n```text\n1 2 3 4 5\n```\n\n### After\n\nWith `-trimEdges 1`:\n\n```text\n2 3 4\n```\n\n## Window\n\nThe `-window \u003cn\u003e` option truncates the original sequence to a fixed frame count. This is helpful for cutting down long animations. Zero indicates no truncation.\n\n### Before\n\n```text\n1 2 3 4 5\n```\n\n### After\n\nWith `-window 3`:\n\n```text\n1 2 3\n```\n\nWith `-window 3` and `-trimStart 1`:\n\n```text\n2 3 4\n```\n\n## Cut Interval\n\nThe `-cutInterval \u003cn\u003e` option removes every nth frame from the original sequence.\n\nThis can mitigate some oscillation, such as lighting fluctuations from fans.\n\n### Before\n\n```text\n1 2 3 4 5 6 7 8\n```\n\n### After\n\nWith `-cutInterval 2`:\n\n```text\n1 3 5 7\n```\n\nThis can also artificially accelerate the perceived speed of the animation. Useful when want to accelerate an animation already scaled down to 2cs per frame.\n\n# SHIFT\n\nThe `-shift \u003coffset\u003e` option performs a circular, leftward shift on the original sequence. This is useful for fine tuning how the GIF's very first cycle presents, before entering successive loops.\n\nZero is the neutral shift. A negative offset indicates rightward shift.\n\n### Before\n\n```text\n1 2 3\n```\n\n### After\n\nWith `-shift 1`:\n\n```text\n2 3 1\n```\n\nWith `-shift -1`:\n\n```text\n3 1 2\n```\n\n# SCALE DELAY\n\nThe `-scaleDelay \u003cfactor\u003e` option adjusts animation speed, by multiplying each frame delay by the given factor.\n\n`1` = `1.0` is the neutral, and default factor.\n\nNegative values reverse the original sequence.\n\nWe can diagram this in terms of the frame delays, expressed in centiseconds. That is, `4cs` indicates 4 centisec = 4/100 sec between advancing to the next frame.\n\n### Before\n\n```text\n4cs 6cs 8cs\n```\n\n### After\n\nWith `-scaleDelay 2`:\n\n```text\n8cs 12cs 16cs\n```\n\nWith `-scaleDelay 0.5`:\n\n```text\n2cs 3cs 4cs\n```\n\nWith `-scaleDelay -1`:\n\n```text\n8cs 6cs 4cs\n```\n\nFor compatibility with a wide range of GIF viewers, the resulting delay is upheld to a lower bound of 2cs.\n\n# LOOP COUNT\n\nThe `-loopCount \u003cn\u003e` option configures the low-level GIF loop counter setting. According to the GIF standard:\n\n* `-1` indicates loop exactly once.\n* `0` indicates infinite, endless looping (default).\n* `n` indicates n replays after the first play = 1 + n total iterations.\n\n### Before\n\n```text\n1 2 3\n```\n\n### After\n\nWith `-loopCount 0`:\n\n```text\n1 2 3 (1 2 3 ...)\n```\n\n# SEE ALSO\n\n* [ffmpeg](https://ffmpeg.org/) edits and converts videos\n* [gifenc.sh](https://github.com/thevangelist/FFMPEG-gif-script-for-bash) converts numerous video formats to animated GIF's\n* [harmonica](https://github.com/mcandre/harmonica) repackages comic archives\n* [ImageMagick](https://imagemagick.org/) converts between multimedia formats, including GIF and WEBP\n* [mkvtools](https://emmgunn.com/wp/mkvtools-home/) edits MKV videos\n* [tigris](https://github.com/mcandre/tigris) provides (Kindle) comic book archival utilities\n* [VLC](https://www.videolan.org/vlc/) plays numerous multimedia formats\n* [webm](https://www.webmproject.org/) supports audio in animation loops\n\n🧈\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcandre%2Fbuttery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcandre%2Fbuttery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcandre%2Fbuttery/lists"}