Definition
Flow is the absorbed working state where challenge and skill are matched and the user loses track of friction. The state requires steady feedback, achievable next steps, and zero unnecessary interruption. Below the skill line the user gets bored; above it the user gets frustrated; on the line the user keeps going.
Why it matters for ShurIQ reports
A reader scanning a brief is in shallow flow at best — the design's job is to keep them there until the recommendation lands. Crisp transitions between sections, pre-loaded next viewports, and progress signifiers (where am I in this brief?) keep an executive moving. Any modal, surprise scroll-jack, or unexpected dialog drops them out of flow and the close-tab probability spikes.
Takeaways
- Pre-render the next likely interaction so the path of least resistance keeps the reader moving.
- Provide unambiguous feedback on every action — silent successes feel like errors.
- Match section depth to expected attention budget; do not place the deepest analysis where fatigue is highest.
- Remove every secondary control that does not serve the primary read path.
Visual motion language
Section transitions resolve as smooth sequence-reveal (250ms ease-in-out per chunk) without page jumps. Numerical scores count-up on first paint to signal computation completing in real time.
Origins
Mihály Csíkszentmihályi, 1975 — introduced the flow construct in Beyond Boredom and Anxiety.
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 · flow · Cavalry scene
// Motion family: center-out radial pulse (concentric squares zooming inward)
// Palette: plum-magenta + 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_flow_";
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 = "#5A1E4D";
var TINTS = ["#6E2A60", "#8C4080", "#A35F8A", "#BF7AA0", "#D6CDB0"]; // dark→cream
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 });
// 6 concentric squares from 480 down to 60
var sizes = [480, 410, 340, 270, 200, 130];
for (var i = 0; i < sizes.length; i++) {
var sq = api.primitive("rectangle", PREFIX + "sq_" + i);
api.set(sq, {
"generator.dimensions": [sizes[i], sizes[i]],
"position.x": 0, "position.y": 0,
"opacity": 0
});
api.setFill(sq, false);
api.setStroke(sq, true);
var tint = TINTS[Math.min(i, TINTS.length - 1)];
api.set(sq, {
"stroke.strokeColor": tint,
"stroke.width": 8
});
var inF = i * 5;
api.keyframe(sq, inF, { "opacity": 0, "scale.x": 0.7, "scale.y": 0.7 });
api.keyframe(sq, inF + 18, { "opacity": 100, "scale.x": 1.0, "scale.y": 1.0 });
api.magicEasing(sq, "scale.x", inF + 18, "EaseOut", "");
// Continuous outward zoom loop
api.keyframe(sq, 60, { "scale.x": 1.0, "scale.y": 1.0 });
api.keyframe(sq, 120, { "scale.x": 1.18, "scale.y": 1.18 });
api.keyframe(sq, 121, { "scale.x": 1.0, "scale.y": 1.0 });
}
// Center cream square
var centerSq = api.primitive("rectangle", PREFIX + "center");
api.set(centerSq, {
"generator.dimensions": [60, 60],
"position.x": 0, "position.y": 0,
"opacity": 0
});
api.setFill(centerSq, true);
api.set(centerSq, { "material.materialColor": CREAM });
api.keyframe(centerSq, 30, { "opacity": 0 });
api.keyframe(centerSq, 50, { "opacity": 100 });
var layerCount = api.getAllSceneLayers().length;
console.log("scene built: flow (" + layerCount + " layers)");
})();