{"id":"ui-morph-loop","version":"1.0.0","title":"UI Morph Loop","summary":"One rounded shape morphs from a button into a loader, a music player, a slider, a toggle, a chart and a search box, driven by a cursor, and loops back to the start.","kind":"motion","target":"canvas2d","collections":["product-reveals","hypnotic-loops","satisfying"],"tags":["ui morph","ui animation","shape morph","one shape","morphing button","micro-interaction","cursor","click","dribbble","product launch","app promo","saas","loop","seamless loop","springs","command palette","music player","toggle","slider","chart","opus 5.5","motion design","2d","canvas"],"loopable":true,"motion":"procedural","durationSeconds":12,"status":"published","media":{"poster":"https://aipixelwizard.com/media/ui-morph-loop/1.0.0/poster.webp","preview":"https://aipixelwizard.com/media/ui-morph-loop/1.0.0/preview.mp4","sample":"https://aipixelwizard.com/media/ui-morph-loop/1.0.0/sample.mp4","sampleBytes":502424,"sampleNote":"Pre-rendered with the default settings. Customised settings must be exported locally (npm run render)."},"urls":{"page":"https://aipixelwizard.com/visuals/ui-morph-loop","manifest":"https://aipixelwizard.com/api/v1/visuals/ui-morph-loop/versions/1.0.0","agentMd":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/agent.md","files":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/","demo":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/index.html"},"description":"The Dribbble-style UI motion loop that took over X in late September 2026: a single element never cuts away. A cursor clicks the button, it shrinks into a spinner, turns into a check, grows into a music player whose play icon splits into pause, the progress bar is dragged and becomes a volume slider that stretches when pulled past the end, a toggle flips with a stretchy knob, the toggle opens into a chart that draws itself with a tooltip, and the chart collapses into a command palette where typing filters the list. The camera zooms so every state fills the frame and the last frame is the first. Pick the sequence of states, your four colours, the button and notification text, how springy it feels, and whether the cursor, labels and motion blur show.","role":"A complete UI motion loop for launch videos, product teasers and portfolio reels (Canvas 2D).","useCases":["Launch video or Product Hunt teaser","Opening of an app promo or SaaS ad","Portfolio or motion showreel loop","Background loop on a landing page"],"synonyms":["UI morph loop","morphing UI","shape morph animation","one shape never cut","button morph","UI motion loop","Dribbble UI animation","launch video","app promo video","product demo animation","micro interactions","fake UI animation","cursor animation"],"digest":"afb0b63ca15ebd3c135ebc73d86a5bf49738a6f8d47b2782eb4cf1728fe6e678","implementation":{"target":"canvas2d","runtime":"browser","language":"javascript","module":"esm","entry":"src/ui-morph-loop.js","exports":[{"name":"createUiMorphLoop","signature":"createUiMorphLoop({ sequence?, cycleSeconds?, label?, toastText?, canvas?, ink?, surface?, accent?, overshoot?, cameraFollow?, cursor?, labels?, background?, motionBlurSamples?, fontFamily? }) => { draw(ctx, time, width, height), drawFrame(ctx, info, width, height), cycleSeconds, beatSeconds, states }","description":"Canvas 2D UI morph loop. drawFrame blends sub-frames for motion blur; draw renders one instant. Both are pure functions of time."},{"name":"springStep","signature":"springStep(x, omega, zeta) => number","description":"Closed-form step response of a damped spring (0 before release, 1 at rest). Sum one per change of target to keep any value a pure function of time."},{"name":"SEQUENCES","signature":"{ tour, launch, music }","description":"The built-in orders of states."}],"reusable":["src/ui-morph-loop.js"],"demo":"demo.js","harness":"index.html"},"dependencies":[{"id":"apw-runtime","version":"1.0.0","scope":"demo","reason":"Demo harness. src/ui-morph-loop.js has no dependencies."}],"uses":[],"usedBy":[],"params":[{"key":"sequence","label":"States","help":"Which UI states the shape morphs through, in order.","type":"select","default":"tour","options":[{"value":"tour","label":"Full tour: button, loader, check, player, slider, toggle, chart, search"},{"value":"launch","label":"Launch: button, loader, check, toast, chart, search"},{"value":"music","label":"Music app: live activity, player, volume, toggle, toast"}]},{"key":"label","label":"Button text","type":"text","default":"Get started","maxLength":24,"sensitive":true},{"key":"toastText","label":"Notification text","type":"text","default":"Changes saved","maxLength":28,"sensitive":true},{"key":"canvas","label":"Background","type":"color","default":"#e8e5df"},{"key":"ink","label":"Dark UI colour","help":"Buttons, player and slider.","type":"color","default":"#111111"},{"key":"surface","label":"Light UI colour","help":"Cards, knob and text on dark parts.","type":"color","default":"#ffffff"},{"key":"accent","label":"Accent colour","help":"The one highlight: check, toggle on, chart line, album art.","type":"color","default":"#ff5b1f"},{"key":"overshoot","label":"Spring overshoot","help":"0 settles with no bounce; 1 overshoots a little on every change.","type":"number","min":0,"max":1,"step":0.05,"default":0.35},{"key":"cameraFollow","label":"Camera follows the shape","help":"Zoom so each state fills the frame.","type":"boolean","default":true},{"key":"cursor","label":"Show cursor","type":"boolean","default":true},{"key":"labels","label":"Show state labels","help":"Small state name, counter and beat dots in the corners.","type":"boolean","default":true},{"key":"motionBlur","label":"Motion blur","help":"Blends five sub-frames per frame. Turn off for faster previews.","type":"boolean","default":true}],"presets":[{"id":"default","label":"Paper and orange","params":{}},{"id":"mono","label":"Pure mono launch","params":{"sequence":"launch","canvas":"#f5f5f4","ink":"#0a0a0a","surface":"#ffffff","accent":"#0a0a0a","overshoot":0.15,"labels":false}},{"id":"night","label":"Night music app","params":{"sequence":"music","canvas":"#0d0d10","ink":"#f2f1ec","surface":"#1c1c21","accent":"#c8f03c","toastText":"Added to library"}},{"id":"cobalt","label":"Cobalt and pink","params":{"canvas":"#2a4bff","ink":"#0b0b0f","surface":"#ffffff","accent":"#ff3d7f","overshoot":0.6,"label":"Try it free"}}],"output":{"width":1080,"height":1080,"fps":30,"durationSeconds":12,"posterSeconds":4.6,"alpha":false,"motion":"procedural","loop":{"seamless":true,"cycleSeconds":12,"method":"Every animated value (size, corner radius, colour, camera zoom, cursor press) is a sum of closed-form spring step responses, one per change of target, summed over the current and the previous cycle; the targets return to the first state, so the values are periodic in the clip length. The cursor path interpolates keyframes cyclically and state contents depend only on time inside their state."},"seed":null},"composition":{"inputs":"Canvas 2D context, time in seconds (or info with time and fps), width, height.","outputs":"Paints the background colour and the whole loop. With background: false it draws on top of what is there (motion blur is then off).","coordinates":"The shape is centred; layout is in units of 1/1000 of the short side, so any aspect ratio works.","lighting":"One soft drop shadow under the shape; flat UI colours.","timing":"Pure function of time; one loop lasts cycleSeconds and the states share it by beats (the full tour is 24 beats, 120 BPM at 12 seconds).","compatibleWith":["kinetic-title","gradient-background","rolling-counter","number-reveal"],"notes":["Button and notification text are treated as private: they are not stored in shared packs unless you choose to include them.","System fonts differ between computers; pass fontFamily and load the font first (document.fonts.load) for identical renders.","Set cycleSeconds to a whole number of bars of your music (at 120 BPM, 12 s is 6 bars) so the state changes land on the beat."]},"reuse":["Cut it to your music: set cycleSeconds to six bars of your track and every state change lands on a beat.","End a launch video on it: let Kinetic Title Reveal put your product name over the last state, or replace the button text with your call to action.","Use the Launch sequence with your accent colour as a looping hero on a landing page or a Product Hunt gallery video.","Copy springStep for your own UI animation: sum one spring per change of target and any value stays a pure function of time."],"integration":{"newProject":"const morph = createUiMorphLoop({ sequence: 'tour', cycleSeconds: 12 }); then for each frame call morph.drawFrame(ctx, { time: frame / fps, fps }, width, height).","existingProject":"Copy src/ui-morph-loop.js. Call draw(ctx, time, width, height) on your own 2D canvas, or pass background: false to draw it over another layer. Add your own state by adding an entry to STATES with its size, corner radius, fill, beats, cursor keys and a draw function."},"performance":{"notes":"A few dozen path and text draws per sub-frame; with motion blur each frame is drawn five times. Real time at 1080p in a browser.","limits":"The content blur uses canvas filter, which Safari before 18 does not support (the swap then fades without blur)."},"limitations":["The UI states are fixed drawings (a music player, a chart, a command list). Changing what they show means editing their draw functions; only the button and notification text are parameters.","It does not play sound. The original trend syncs clicks and whooshes to music; add your own audio in the edit.","Single-line, left-to-right text. Long labels are shrunk to fit rather than wrapped."],"license":{"spdx":"MIT","notice":"Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.","thirdParty":[]},"rights":{"approvedForPublicRelease":true},"provenance":"Original code written for AI Pixel Wizard on 2026-10-05 by an automated agent (the daily trend routine), inspired by the UI morph loop trend on X (see content/trends/2026-10-05-ui-morph-loop.md), started by @twoclipping's open prompt (https://x.com/twoclipping/status/2103273003555402193). The ideas of one shape that never cuts, a cursor driving each change, a camera that zooms to each state, a tab or knob whose two edges ride different springs, and values built as sums of closed-form spring step responses come from that prompt's text; no code from the post, its replies or any remix was opened or copied. All UI drawings, icons, chart data and the cursor are generated by this code; no images, fonts or icon sets are bundled.","changelog":[{"version":"1.0.0","date":"2026-10-05","notes":"Initial release."}],"verification":{"testedOn":"2026-10-06","inputs":{"ui-morph-loop@1.0.0":"afb0b63ca15ebd3c135ebc73d86a5bf49738a6f8d47b2782eb4cf1728fe6e678","apw-runtime@1.0.0":"5be859cdac74823ba0bcc32c6daf06688a871104d24db5d4ceaffffd84d7c40d"},"environment":"Chrome 154.0.8037.97 headless on Linux 7.0.0-38-generic (x64), Node 22.14.0","renderer":"Canvas 2D","width":1080,"height":1080,"fps":30,"frames":360,"renderSeconds":1832.9,"loopCheck":{"checked":true,"seamless":true,"maxDifference":0,"note":"Frame 0 vs end state (progress 1): mean |Δ| 0.0000, max |Δ| 0 of 255."},"determinism":{"checked":true,"identical":true},"errors":[],"media":{"poster":"/media/ui-morph-loop/1.0.0/poster.webp","preview":"/media/ui-morph-loop/1.0.0/preview.mp4","sample":"/media/ui-morph-loop/1.0.0/sample.mp4","sampleBytes":502424}},"files":[{"path":"LICENSE","size":1072,"sha256":"8a5f3a96f07e11522ec4bdd0a583356f99bad1245920f9709cee09b995a45153","contentType":"text/plain; charset=utf-8","url":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/LICENSE"},{"path":"asset.json","size":11880,"sha256":"362608c1730e7b59e5d98838e16288fdcd44fdcff3d00efe87b2a9c388ea8c38","contentType":"application/json; charset=utf-8","url":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/asset.json"},{"path":"demo.js","size":952,"sha256":"f532ce7f9c3bcd575194fcb224a8b37607c8a883bfb925d13932fb6303c4a037","contentType":"text/javascript; charset=utf-8","url":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/demo.js"},{"path":"index.html","size":527,"sha256":"441e81f347099f4639cf755604e7c81b5c634aede93ea32258cbfcaa210d3b8f","contentType":"text/html; charset=utf-8","url":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/index.html"},{"path":"src/ui-morph-loop.js","size":35493,"sha256":"85ec1909b8751fe74277a6b7bb7cc6e9a9e60fa608ae155e9bf93df5f4e0b639","contentType":"text/javascript; charset=utf-8","url":"https://aipixelwizard.com/registry/ui-morph-loop/1.0.0/src/ui-morph-loop.js"}],"warnings":[]}