Effects / SVG
Risograph print
Flat spot-color shapes with slight misregistration and grain, like a risograph poster.
Prompt to build it
Build a risograph-style sun-and-sea poster. Use SVG shapes in fluorescent pink, blue and yellow with mix-blend-mode multiply, a slight drifting misregistration, and an feTurbulence grain overlay. Make it responsive, cap devicePixelRatio at 2, pause when off-screen, and show a still frame when prefers-reduced-motion is set. No external libraries.
Tested code
One self-contained HTML file with no libraries: the same code that runs above. Your AI gets it from Flow's get_effect tool.
Show the code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Risograph print | Flow effect</title>
<!--
Flow effect: Risograph print (https://flow.donta.io/effects/risograph-print)
Self-contained: no libraries. Copy the .fx-stage element, the CSS and the
script into your project and size .fx-stage however you like.
-->
<style>
:root { --display: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif; --body: ui-sans-serif, system-ui, sans-serif; }
html, body { margin: 0; min-height: 100%; background: #0e0e10; display: grid; place-items: center; }
.fx-stage { width: min(100vw, 960px); }
/* Stage that every effect fills. Container units (cqw/cqmin) size the art. */
.fx-stage {
position: relative;
aspect-ratio: 4 / 3;
overflow: hidden;
border-radius: 16px;
container-type: size;
background: #111;
isolation: isolate;
}
.fx-fill {
position: absolute;
inset: 0;
}
.fx-fallback {
position: absolute;
inset: 0;
}
.fx-center {
display: grid;
place-items: center;
}
/* ── Risograph ────────────────────────────────── */
.fx-riso { background: #f4ecd8; }
.fx-riso svg { width: 100%; height: 100%; }
.fx-riso .ink { mix-blend-mode: multiply; }
.fx-riso .drift-a { animation: fx-misreg 2.4s steps(3) infinite alternate; }
.fx-riso .drift-b { animation: fx-misreg 3.1s steps(3) infinite alternate-reverse; }
.fx-riso .sun-rays { transform-box: view-box; transform-origin: 100px 62px; animation: fx-spin 30s linear infinite; }
@keyframes fx-misreg { to { translate: 1.5px -1px; } }
@keyframes fx-spin { to { rotate: 360deg; } }
@media (prefers-reduced-motion: reduce) {
.fx-stage *, .fx-stage *::before, .fx-stage *::after { animation-play-state: paused !important; }
}
</style>
</head>
<body>
<div class="fx-stage">
<div class="fx-fill fx-riso"><svg viewBox="0 0 200 150" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
<defs><filter id="riso-grain"><feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" stitchTiles="stitch"/><feColorMatrix values="0 0 0 0 0.3 0 0 0 0 0.25 0 0 0 0 0.2 0 0 0 0.2 0"/></filter></defs>
<g class="ink drift-a sun-rays" fill="#ff4a3d"><rect x="98" y="14" width="4" height="14" transform="rotate(0 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(30 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(60 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(90 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(120 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(150 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(180 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(210 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(240 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(270 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(300 100 62)"/><rect x="98" y="14" width="4" height="14" transform="rotate(330 100 62)"/></g>
<circle class="ink drift-a" cx="100" cy="62" r="28" fill="#ff4a3d"/>
<path class="ink drift-b" d="M0 98 Q25 88 50 98 T100 98 T150 98 T200 98 V150 H0Z" fill="#2f6fdb"/>
<path class="ink drift-a" d="M0 112 Q25 104 50 112 T100 112 T150 112 T200 112 V150 H0Z" fill="#ffd23f" opacity="0.85"/>
<text class="ink drift-b" x="16" y="24" font-size="16" font-weight="800" fill="#2f6fdb" font-family="var(--display, sans-serif)">RISO</text>
<rect width="200" height="150" filter="url(#riso-grain)"/>
</svg></div>
</div>
</body>
</html>
How it works
Overlap two or three spot-color layers with mix-blend-mode: multiply, offset them by a pixel or two that drifts over time, and add an SVG noise grain.
Guardrails
- Keep misregistration small (≤3px) so edges stay readable.
- Respect prefers-reduced-motion: show a still frame instead of the animation.
- Pause when off-screen or when the tab is hidden (IntersectionObserver + visibilitychange).
- Keep text off busy animated areas, or give it a solid backing that passes WCAG contrast.
- One showpiece effect per page. If everything moves, nothing stands out.