Definition
Visually similar elements are perceived as members of the same group — even when separated by space, content, or time. Color, shape, weight, and motion behavior all serve as group signals. Similarity is the second-strongest grouping force after proximity.
Why it matters for ShurIQ reports
Stack-rank rows, brand-power-score axes, and viewport-tile types each need a consistent visual identity that the reader can re-acquire on every scroll. If two unrelated UI elements share styling, the reader will spend cognitive effort wondering whether they are linked. The discipline is to keep similar things similar and different things visibly different.
Takeaways
- Encode role through visual properties: links one color, primary actions another, navigation a third — never overlap.
- When two items must look different despite being structurally identical (e.g., a featured row), shift one property strongly rather than several weakly.
- Use motion behavior as a grouping signal — items that animate the same way will be read as the same kind of thing.
- Audit for accidental similarity: a card and a button using the same shadow read as siblings even if they are unrelated.
Visual motion language
Items of the same class animate in parallel-rays — same easing, same delay offset — to reinforce membership. Color-emphasis sweeps share timing across grouped elements.
Origins
Gestalt psychology — early 20th century perceptual organization research.
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 · law-of-similarity · Cavalry scene
// CORRECTED 2026-04-30 · verified from production page
// Motion family: grid-stagger (5x5 identical circles, 7 cream scattered, similarity by color only)
// No connecting lines. Row-by-row top-to-bottom reveal.
// Palette: pink/maroon + 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_law-of-similarity_";
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 = "#8C3A4F";
var CREAM = "#D6CDB0";
var DARK = "#4A1F2A";
var bg = api.primitive("rectangle", PREFIX + "bg");
api.set(bg, { "generator.dimensions": [1080, 1080] });
api.setFill(bg, true);
api.set(bg, { "material.materialColor": BG });
// 5x5 grid. Cream coords (1-indexed row,col): (1,1)(1,3)(2,4)(3,3)(4,1)(4,5)(5,2)
var creamMap = {
"1,1": true, "1,3": true,
"2,4": true,
"3,3": true,
"4,1": true, "4,5": true,
"5,2": true
};
var R = 48; // radius
var SPACING = 130; // center-to-center
var GRID = 5;
var origin = -((GRID - 1) * SPACING) / 2; // -260 -> first col
for (var r = 1; r <= GRID; r++) {
for (var c = 1; c <= GRID; c++) {
var key = r + "," + c;
var isCream = !!creamMap[key];
var dot = api.primitive("ellipse", PREFIX + "r" + r + "c" + c);
api.set(dot, {
"generator.radius": [R, R],
"position.x": origin + (c - 1) * SPACING,
"position.y": -(origin + (r - 1) * SPACING), // flip: row 1 at top (+Y)
"opacity": 0
});
api.setFill(dot, true);
api.set(dot, { "material.materialColor": isCream ? CREAM : DARK });
// Row-by-row stagger: ~140ms per row (~4 frames at 30fps)
var t0 = (r - 1) * 4;
var t1 = t0 + 10;
api.keyframe(dot, t0, { "opacity": 0 });
api.keyframe(dot, t1, { "opacity": isCream ? 100 : 45 });
api.magicEasing(dot, "opacity", t1, "EaseOut", "");
}
}
var layerCount = api.getAllSceneLayers().length;
console.log("scene built: law-of-similarity (" + layerCount + " layers)");
})();