Definition
Each user carries a compressed internal model of how a system works, built from prior experience with similar systems. They apply this model to new products on first contact — and any mismatch between the user's model and the actual system shows up as confusion, error, or abandonment. Designing without considering mental models guarantees friction.
Why it matters for ShurIQ reports
A CMO's mental model of "competitive intelligence" is shaped by McKinsey decks, Pitchbook tables, and Bloomberg terminals. The brief must use the metaphors they already hold — stack rank, score, evidence — and reserve novelty for the analytical content. The Brand Power Score earns trust because executives already know how composite scores work; the SBPI methodology earns adoption because evidence trails behave like footnotes.
Takeaways
- Map the reader's existing mental model before introducing any new pattern.
- When introducing a new construct (consensus floor, viewport composition), anchor it to a known reference point.
- Reduce designer-user model gap through user research; never assume the team's model matches the reader's.
- Evolve patterns gradually across recurring reports — each iteration should feel like a refinement, not a relaunch.
Visual motion language
Familiar patterns enter with quiet fade-in and snap-grid alignment. Novel constructs use sequence-reveal to walk the reader through the unfamiliar logic before settling into static state.
Origins
Kenneth Craik, 1943 — The Nature of Explanation.
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 · mental-model · Cavalry scene
// Motion family: single-form (rotating hex wireframe)
// Palette: olive-mustard + 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_mental-model_";
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 = "#8C7C45";
var DARK = "#6E5F2F";
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 });
// Backing hex (filled, depth)
var backHex = api.primitive("polygon", PREFIX + "back_hex");
api.set(backHex, {
"generator.sides": 6, "generator.radius": 240,
"position.x": 0, "position.y": 0,
"opacity": 0
});
api.setFill(backHex, true);
api.set(backHex, { "material.materialColor": DARK });
api.keyframe(backHex, 0, { "opacity": 0 });
api.keyframe(backHex, 18, { "opacity": 40 });
// Outline hex
var outHex = api.primitive("polygon", PREFIX + "out_hex");
api.set(outHex, {
"generator.sides": 6, "generator.radius": 220,
"position.x": 0, "position.y": 0,
"opacity": 0
});
api.setFill(outHex, false);
api.setStroke(outHex, true);
api.set(outHex, { "stroke.strokeColor": CREAM, "stroke.width": 4 });
api.keyframe(outHex, 6, { "opacity": 0 });
api.keyframe(outHex, 22, { "opacity": 100 });
// Slow rotation
api.keyframe(outHex, 0, { "rotation.z": 0 });
api.keyframe(outHex, 144, { "rotation.z": 360 });
// 6 vertex nodes
var R = 200;
var nodeIds = [];
for (var v = 0; v < 6; v++) {
var theta = (v / 6) * Math.PI * 2 - Math.PI / 2;
var ex = Math.cos(theta) * R;
var ey = Math.sin(theta) * R;
var nd = api.primitive("ellipse", PREFIX + "vert_" + v);
api.set(nd, {
"generator.radius": [22, 22],
"position.x": ex, "position.y": ey,
"opacity": 0,
"scale.x": 0.4, "scale.y": 0.4
});
api.setFill(nd, true);
api.set(nd, { "material.materialColor": CREAM });
api.keyframe(nd, 18 + v * 2, { "opacity": 0, "scale.x": 0.4, "scale.y": 0.4 });
api.keyframe(nd, 30 + v * 2, { "opacity": 100, "scale.x": 1.0, "scale.y": 1.0 });
api.magicEasing(nd, "scale.x", 30 + v * 2, "EaseOut", "");
api.magicEasing(nd, "scale.y", 30 + v * 2, "EaseOut", "");
nodeIds.push({ id: nd, x: ex, y: ey, theta: theta });
}
// 6 spokes from center to each vertex
for (var v = 0; v < 6; v++) {
var info = nodeIds[v];
var spoke = api.primitive("rectangle", PREFIX + "spoke_" + v);
var len = R;
api.set(spoke, {
"generator.dimensions": [len, 3],
"position.x": info.x / 2,
"position.y": info.y / 2,
"rotation.z": info.theta * (180 / Math.PI),
"opacity": 0,
"scale.x": 0.001
});
api.setFill(spoke, true);
api.set(spoke, { "material.materialColor": CREAM });
api.keyframe(spoke, 24 + v * 2, { "opacity": 100, "scale.x": 0.001 });
api.keyframe(spoke, 38 + v * 2, { "opacity": 100, "scale.x": 1 });
}
// Center node
var center = api.primitive("ellipse", PREFIX + "center");
api.set(center, {
"generator.radius": [18, 18],
"position.x": 0, "position.y": 0,
"opacity": 0
});
api.setFill(center, true);
api.set(center, { "material.materialColor": CREAM });
api.keyframe(center, 16, { "opacity": 0 });
api.keyframe(center, 28, { "opacity": 100 });
var layerCount = api.getAllSceneLayers().length;
console.log("scene built: mental-model (" + layerCount + " layers)");
})();