LAWSOFUX · motion lab
Concept · millers-law

Miller's Law

Grid stagger reveal palette · warm tan / cream takeaways · 3

Definition

Working memory holds about 7±2 discrete items at once, and the number contracts under stress, fatigue, or context-switching. The number is not a design quota; it is a ceiling that drops fast in real-world conditions. Design for the lower bound, not the average.

Why it matters for ShurIQ reports

Stack-rank views, KPI strips, and filter rails should respect a working-memory budget. Five competitor cards on screen are easy to compare; eleven require the reader to scroll, lose context, and re-anchor. Where breadth is necessary, lean on chunking to compress many items into a small number of visual groups.

Takeaways

Visual motion language

Lists reveal in groups of 5–7 with a sequence-reveal cadence; additional items load on demand with a discrete group-form transition that signals a new chunk.

Cavalry recreation seed. 7×7 grid of 36px dots, 50px spacing. All dots filled dark terracotta. Animate row 4 dots: each dot fades from dark→cream over 300ms with stagger from center outward (center first at t=0, ±1 at t=180ms, ±2 at t=360ms, ±3 at t=540ms). The outermost ones reach only 30% opacity to model 7±2. Hold 2s. Reset.

Origins

George A. Miller, 1956 — The Magical Number Seven, Plus or Minus Two.

Cavalry scene

The script below builds this concept's motion in Cavalry through the Stallion bridge. Pipe to cavalry_run_script via MCP, or paste into Cavalry's JavaScript Editor.

// Laws of UX · millers-law · Cavalry scene
// Motion family: grid-stagger (7±2 center-spread highlight)
// Palette: terracotta + cream
// To run: pipe to cavalry_run_script tool, or paste into Cavalry's JavaScript Editor
// Built 2026-04-30 by ShurAI

(function () {
  var PREFIX = "claude_lawofux_millers-law_";

  var existing = api.getAllSceneLayers();
  for (var i = 0; i < existing.length; i++) {
    try {
      var nm = api.getNiceName(existing[i]);
      if (nm && nm.indexOf("claude_lawofux_") === 0) api.deleteLayer(existing[i]);
    } catch (e) {}
  }

  var BG    = "#B8442E";
  var DARK  = "#8E3322";
  var CREAM = "#D6CDB0";

  var bg = api.primitive("rectangle", PREFIX + "bg");
  api.set(bg, { "generator.dimensions": [1080, 1080] });
  api.setFill(bg, true);
  api.set(bg, { "material.materialColor": BG });

  // 7x7 grid
  var COLS = 7, ROWS = 7, SPACE = 65, R = 16;
  var startX = -((COLS - 1) * SPACE) / 2;
  var startY =  ((ROWS - 1) * SPACE) / 2;

  for (var r = 0; r < ROWS; r++) {
    for (var c = 0; c < COLS; c++) {
      var d = api.primitive("ellipse", PREFIX + "dot_" + r + "_" + c);
      api.set(d, {
        "generator.radius": [R, R],
        "position.x": startX + c * SPACE,
        "position.y": startY - r * SPACE,
        "opacity": 0
      });
      api.setFill(d, true);
      api.set(d, { "material.materialColor": DARK });

      // Initial fade-in
      var inF = (r * COLS + c);
      api.keyframe(d, inF,      { "opacity": 0   });
      api.keyframe(d, inF + 8,  { "opacity": 100 });

      // Highlight middle row (r=3) — center brightest, edges dimmest
      if (r === 3) {
        var distFromCenter = Math.abs(c - 3);
        var targetBrightness = [100, 80, 50, 25][distFromCenter] || 0;
        var highlightFrame = 60 + distFromCenter * 4;

        // Keep dark fill but add a cream overlay dot
        var hl = api.primitive("ellipse", PREFIX + "hl_" + c);
        api.set(hl, {
          "generator.radius": [R, R],
          "position.x": startX + c * SPACE,
          "position.y": startY - 3 * SPACE,
          "opacity": 0
        });
        api.setFill(hl, true);
        api.set(hl, { "material.materialColor": CREAM });
        api.keyframe(hl, highlightFrame,      { "opacity": 0 });
        api.keyframe(hl, highlightFrame + 10, { "opacity": targetBrightness });
      }
    }
  }

  var layerCount = api.getAllSceneLayers().length;
  console.log("scene built: millers-law (" + layerCount + " layers)");
})();