LAWSOFUX · motion lab
Concept · zeigarnik-effect

Zeigarnik Effect

Opacity-stepped bands palette · cobalt / cream takeaways · 3

Definition

Unfinished tasks linger in memory more vividly than completed ones — the brain holds open loops and pushes the user to close them. Designs that make the next step visible recruit this effect; designs that hide what comes next lose it.

Why it matters for ShurIQ reports

Multi-viewport dashboards and progressive intelligence briefs both benefit from visible open loops: a partial gap card that reveals more on click, a "5 of 7 viewports viewed" indicator, a teased next section at the foot of the current one. The reader's brain holds the unfinished read open until the recommendation closes it.

Takeaways

Visual motion language

Incomplete sections show a subtle marching-ants edge or a pulsing indicator; on completion, the marker resolves to a steady fade-in confirming closure.

Cavalry recreation seed. 7 horizontal stripes inside a 566×566 panel. Stripes 1–6 (top to second-to-last): full-canvas-width 566×68, fill dark (#000 at 0.2 opacity, reads as muted on the dark-navy bg). Stripe 7 (bottom): width 295×68 (truncated to ~52% of canvas), fill solid cream (#f4f1d0). Vertical positions y = 0, 83, 166, 249, 332, 415, 498. Animate sequential top-to-bottom fade-in, ~120ms per stripe, ~840ms total. The truncated cream stripe at the end remains visually unresolved; hold 2.5s; loop. Background: dark navy #1F2C3A. Replaces the concentric-arc guess.

Origins

Bluma Wulfovna Zeigarnik, 1920s — Lithuanian-Soviet psychologist's foundational memory study.

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 · zeigarnik-effect · Cavalry scene
// CORRECTED 2026-04-30 · verified from production page
// Motion family: opacity-stepped horizontal bands
// 7 stripes; stripes 1-6 muted; stripe 7 (bottom) cream/full-opacity AND truncated to 52% width
// (incompleteness encoded as horizontal width-truncation, NOT a 90° arc gap)
// Palette: dark navy + cream/purples
// 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_zeigarnik-effect_";

  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    = "#1F2C3A";
  var CREAM = "#D6CDB0";
  var DARK  = "#000000";

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

  // 7 stripes total. Geometry mirrors verified 566x68 stripe + 15px gutter -> scaled to canvas:
  //   full width = 880, stripe height = 100, gutter = 22 (step=122)
  var fullW   = 880;
  var stripeH = 100;
  var step    = 122; // 100 + 22
  var topY    = (6 * step) / 2; // top stripe center

  // Stripes 1-6: muted, full width
  for (var j = 0; j < 6; j++) {
    var s = api.primitive("rectangle", PREFIX + "stripe_" + j);
    api.set(s, {
      "generator.dimensions": [fullW, stripeH],
      "position.x": 0,
      "position.y": topY - j * step,
      "opacity": 0
    });
    api.setFill(s, true);
    api.set(s, { "material.materialColor": DARK });

    var t0 = j * 4;
    var t1 = t0 + 8;
    api.keyframe(s, t0, { "opacity": 0 });
    api.keyframe(s, t1, { "opacity": 22 }); // ~0.22 alpha-on-bg reads as muted
    api.magicEasing(s, "opacity", t1, "EaseOut", "");
  }

  // Stripe 7: cream, full opacity, truncated to ~52% width and left-aligned
  var truncW = Math.round(fullW * 0.52); // 458
  var leftEdge = -fullW / 2;             // -440
  var s7 = api.primitive("rectangle", PREFIX + "stripe_6");
  api.set(s7, {
    "generator.dimensions": [truncW, stripeH],
    "position.x": leftEdge + truncW / 2, // left-aligned
    "position.y": topY - 6 * step,
    "opacity": 0
  });
  api.setFill(s7, true);
  api.set(s7, { "material.materialColor": CREAM });
  api.keyframe(s7, 24, { "opacity": 0   });
  api.keyframe(s7, 32, { "opacity": 100 });
  api.magicEasing(s7, "opacity", 32, "EaseOut", "");

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