LAWSOFUX · motion lab
Concept · choice-overload

Choice Overload

Grid stagger reveal palette · cobalt / cream takeaways · 3

Definition

When a user faces too many simultaneous options, decision quality collapses and the user disengages or defers. The cost is not deciding wrong; the cost is not deciding at all. The phrase travels under "paradox of choice," but the operational behavior is paralysis.

Why it matters for ShurIQ reports

A 21-company stack rank can become noise unless the viewport collapses into the 3–5 entries a reader can actually act on. Featured rows, default sorts, and progressive disclosure on competitor cards keep an executive moving through the brief instead of stalling on row eleven. The dashboard's job is to pre-decide what to show first.

Takeaways

Visual motion language

Show the long list first, then collapse non-priority rows to half-height with a 300ms ease-out as the priority rows zoom and the reader's focus narrows. The collapse should feel like the system is helping the reader decide.

Cavalry recreation seed. 5×4 grid of 60px circles spaced 90px apart inside a 540×400 area. Alternate fills cream / dark-navy (checkerboard). Animate ghost row of 5 lower-opacity dots fading in below from the right with 200ms stagger between dots, total ~1.4s. Idle 1s, fade out, repeat.

Origins

Alvin Toffler, 1970 — introduced in Future Shock under the term "overchoice."

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 · choice-overload · Cavalry scene
// Motion family: grid-stagger
// Palette: cobalt + 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_choice-overload_";

  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    = "#1E5C8C";
  var DARK  = "#163E5C";
  var CREAM = "#D6CDB0";

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

  // 5-cols × 4-rows grid of dots, 90px spacing, 60px diameter
  var COLS = 5, ROWS = 4, SPACE = 100, R = 30;
  var startX = -((COLS - 1) * SPACE) / 2;
  var startY =  ((ROWS - 1) * SPACE) / 2;

  var idx = 0;
  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);
      var fill = ((r + c) % 2 === 0) ? CREAM : DARK;
      api.set(d, { "material.materialColor": fill });

      var inFrame = idx * 2;            // ~80ms stagger
      var endFrame = inFrame + 8;       // 333ms fade
      api.keyframe(d, inFrame, { "opacity": 0 });
      api.keyframe(d, endFrame, { "opacity": 100 });
      idx++;
    }
  }

  // Ghost overflow row below
  for (var g = 0; g < 5; g++) {
    var gd = api.primitive("ellipse", PREFIX + "ghost_" + g);
    api.set(gd, {
      "generator.radius": [R, R],
      "position.x": startX + g * SPACE,
      "position.y": startY - ROWS * SPACE,
      "opacity": 0
    });
    api.setFill(gd, true);
    api.set(gd, { "material.materialColor": CREAM });
    var f0 = 50 + g * 5;
    var f1 = f0 + 12;
    api.keyframe(gd, f0, { "opacity": 0 });
    api.keyframe(gd, f1, { "opacity": 35 });
  }

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