Skip to content
animorio
IT
All components

The palette kaleidoscope

Loading, Physics, TransitionsCanvas, GSAPAdvancedCompressed weight: Demo 15 KBGSAP 28 KBFreeDownload the zip

A design app’s palette generated from a moodboard as a kaleidoscope: every colour it reads falls into the chamber as a chip of glass, tumbles over the others while the drum turns, and two mirrors multiply it into eight slices. The fuller the chamber, the richer the pattern; at the end the drum stops and the five chosen colours come out of the eyepiece as swatches.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <title>The palette kaleidoscope</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the palette generator of a design app: every colour read from the moodboard drops into a kaleidoscope as a
         chip of glass, tumbles in the turning chamber and is mirrored into eight slices. When the chamber is full the
         drum stops and the five colours it settled on come out as swatches below. The glass is simulated and painted
         on the canvas by script.js; the words, the swatches and the button are HTML -->
    <section class="pal" aria-labelledby="pal-title" data-pal>
      <div class="pal__top">
        <div class="pal__scope" role="progressbar" aria-labelledby="pal-title" aria-valuemin="0" aria-valuemax="100"
             aria-valuenow="0" aria-valuetext="Palette not generated yet" data-scope>
          <div class="pal__knurl" aria-hidden="true" data-knurl></div>
          <div class="pal__well">
            <canvas class="pal__glass" aria-hidden="true" data-glass></canvas>
            <span class="pal__lens" aria-hidden="true"></span>
          </div>
        </div>
        <div class="pal__words">
          <h2 class="pal__title" id="pal-title">Moodboard · Coastal Dusk</h2>
          <p class="pal__big" aria-hidden="true"><span class="pal__swap" data-big>24 images</span></p>
          <p class="pal__status">
            <span class="pal__swap" data-status>No palette yet</span>
            <svg class="pal__tick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.6 10.4l3.5 3.5 7.4-8" pathLength="1"/></svg>
          </p>
          <p class="pal__sub"><span class="pal__swap" data-sub>Edited 2 h ago</span></p>
          <button class="pal__btn" type="button" data-action data-autoclic><span class="pal__label" data-label>Generate palette</span></button>
        </div>
      </div>
      <ul class="pal__swatches" aria-label="Generated palette" data-swatches>
        <li class="pal__slot"><span class="pal__chip" data-chip></span><span class="pal__hex" data-hex></span></li>
        <li class="pal__slot"><span class="pal__chip" data-chip></span><span class="pal__hex" data-hex></span></li>
        <li class="pal__slot"><span class="pal__chip" data-chip></span><span class="pal__hex" data-hex></span></li>
        <li class="pal__slot"><span class="pal__chip" data-chip></span><span class="pal__hex" data-hex></span></li>
        <li class="pal__slot"><span class="pal__chip" data-chip></span><span class="pal__hex" data-hex></span></li>
      </ul>
      <p class="sr" role="status" aria-live="polite" data-live></p>
    </section>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

35 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The palette kaleidoscope” from Animorio and fit it into my site

What it does

On a black background, alone in the middle, a graphite card with round corners. On the left the eyepiece of a kaleidoscope inside a knurled steel drum: inside it, a few chips of clear glass already make a grey, glossy star. On the right Moodboard · Coastal Dusk, the big 24 images, No palette yet, Edited 2 h ago and the pale Generate palette button; below, five empty places for the swatches. Pressed, the button becomes an outlined Generating…, the drum starts to turn (its knurling runs round) and the big figure climbs as a percentage while the line below says how many images have been read. For every share read, a chip of coloured glass, in a shade of the moodboard’s five colours, drops into the chamber: in the eyepiece it appears in the middle with a flash and slides out towards the rim, eight times over. The chips tumble, overlap and mix their light where they cover each other; the drum drags the heap up its wall until it slides back, and the pattern keeps changing. The more glass comes in, the denser the pattern. With the last chip the words say Choosing colours, the drum slows to a stop and the glass settles; a glint crosses the eyepiece and the five chosen colours come out of the middle of the kaleidoscope one after another, fly to their places turning and stop with a bounce, with their hex code below. A green tick is drawn beside Palette ready, the big words say 5 colours and the button becomes Regenerate: the swatches go back in, the coloured glass falls out through the bottom of the chamber and a new palette starts. One time in two, in the demo, the moodboard stops answering halfway: the drum stops with a jolt, the glass jumps, the eyepiece goes grey and dark, Couldn’t read 3 images in red says how many images were read, and Retry brings the colours back and carries on from there.

How it works

A chip of glass is a polygon full of circles

Every chip is an irregular convex polygon (a three sided shard, a quad, a pentagon or a long sliver) centred on its centroid. For the physics it carries a few circles that fill it: one at the centroid and others towards the corners, each as big as its distance to the nearest edge. Mass and moment of inertia come from the area. So collisions between chips become collisions between circles, simple and stable, yet a knock on a corner spins the chip as a real polygon would.

Collisions, solved with impulses

Three physics steps per frame (at most 1/30 of a second in all). Every step gathers the contacts between circles of different chips and between circles and the round wall; then ten rounds of sequential impulses: an impulse along the normal that never pulls (accumulated and kept above zero), with a little bounce when the hit is hard and a small push that undoes overlaps, and friction along the contact that never exceeds FRICTION times the normal impulse. Speeds are capped and no chip can leave through the wall.

The turning drum

The chamber’s wall does not stand still: at the point of contact it moves at the drum’s speed, ω × r, and friction passes it on to the glass. That is why the heap climbs the wall until the slope beats the friction and it slides back, just as in a real kaleidoscope’s tube. The drum’s speed is a GSAP tween: it starts on a soft curve, stops with a jolt when something goes wrong and slows down gently when the palette is ready. The knurling on the drum turns by the same angle while the light on it stays put.

The glass: light that adds up

Every frame the chamber is painted once on a canvas of its own. Every chip is filled with its colour in screen mode, so where two chips overlap their light adds up and the colour brightens; on top, a gradient along the direction of the light gives the thickness (bright where the light comes in, dark where it leaves), a lighter band just inside the outline makes thick cut glass, and every edge lights up or darkens by how much it faces the light.

The mirrors: slices clipped and flipped

The mirrors meet a little below the middle of the chamber and look down, where the glass gathers. The eyepiece draws the chamber’s canvas SLICES times: every slice is a wedge clip of angle 2π/SLICES, turned into place, and every other slice is flipped, so the edges meet as they do between two real mirrors. On top go the faint seams of the mirrors, the grey veil of the error (saturation mode) and the final glint.

For those who cannot see it and those who want no motion

The eyepiece is a role=progressbar with aria-valuenow and an aria-valuetext that says how many images have been read, the card has aria-busy while it works and an aria-live line says when the palette starts, stops and which colours it chose. The swatches are a list with their codes written out. The button changes its word (Generate palette, Generating…, Retry, Regenerate) and takes the new width with a little bounce; while it works it is aria-disabled.

Values to change

NameFileDefaultWhat it changes
IMAGESscript.js24How many images the moodboard has.
SECONDSscript.js6How long a palette takes in the demo, in seconds (a real one follows the generator).
PIECESscript.js34How many chips of coloured glass fill the chamber.
SLICESscript.js8How many mirrored slices the eyepiece shows (an even number).
DRUMscript.js0.8How fast the drum turns while it works, in radians a second.
GRAVITYscript.js1100How hard the glass falls, in pixels a second squared.
FRICTIONscript.js0.35How much the glass grips the wall and the other chips: higher, the heap climbs further before it slides.
BOUNCEscript.js0.18How much a chip bounces when it lands: 0 not at all, 0.6 a lot.
CHIPscript.js1How big the chips are: 0.7 small, 1.3 large.
FAIL_EVERYscript.js2In the demo, one palette in how many cannot read the moodboard (0 never).
FAIL_ATscript.js0.55How far the palette that stops gets, from 0 to 1.
SHEENscript.js0.8The glint that crosses the eyepiece when the palette is ready: 0 none, 1.5 bright.

Try them in the Playground

When to use it

For a wait of a few seconds while a program picks colours: a palette from a moodboard or a photo, a generated theme for an app, a brand’s colours pulled from a website. The kaleidoscope says at once that the work is about colour, and the pattern growing richer makes the result feel as if it is taking shape; for any loading that is not about colour the metaphor is out of place.

On phones

The card takes the screen’s width less 32 pixels (358 on a 390 screen) and stays 266 tall; the eyepiece drops from 184 to 164 pixels, the big words to 27, and the five swatches close up with gaps of 6. The chamber resizes with the eyepiece and the glass keeps its place. The canvas paints at the screen’s real density, up to twice. The button is a pill 36 tall: under a finger it squashes and springs back, and its words widen on the width axis only under the mouse.

With reduced motion

The drum does not turn and the glass does not fall: every new chip fades in already in its place in the heap (the physics settles it out of sight before it is shown), without flashes. No jolt or jump on the error (the grey veil stays), no glint at the end; the swatches fade in at their places and the coloured glass fades out on Regenerate. The pattern still grows richer, because it is what says how far along things are.

Pitfalls

  • The circles that fill the chips came out huge: the distance to the outline took the farthest edge instead of the nearest, and the glass floated scattered round the chamber, held apart by invisible circles. It is now the least distance with the right sign, and the chips heap up.
  • With a fast drum and a lot of friction the heap stayed stuck to the wall at nine o’clock and the mirrors’ wedge, at the bottom, saw little glass: the drum turns at 0.8 radians a second with friction 0.35, and the heap slides back before it leaves the wedge.
  • With the mirrors meeting at the middle of the chamber the pattern stayed empty in the middle until the end: they now meet below the middle, at 0.3 of the radius, where the glass really gets to.
  • Round glass beads, multiplied by the mirrors, made rows of dots in the middle: there are only chips with corners.
  • The hex codes under the swatches looked typewritten: at 11 pixels the font picks its own cut for small text, wide and ink trapped. There the cut is fixed to the one for headings.
  • With many large chips the top of the chamber stayed full and the last chip never found a free spot: the palette sat at 100% for ever. Now, after half a second of waiting, the chip goes in anyway and the collisions make room for it.
  • One line of the error reached the edge of the card and would have spilled out on a phone: it is shorter now.

Libraries

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 15 KB; the libraries: GSAP 28 KB.

Testing

Tested on 8 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All components