Hyper Flow options are designed to compose. Start with a field topology, choose how each point is painted, then add interaction and frame behavior.
Fine orbital shell
Use the orb field with line particles for a dense optical surface similar to the landing page instrument.
import { createParticleFlow } from "@scorpion7slayer/hyper-flow";
const orb = createParticleFlow("#orb", {
mode: "orb",
density: 2.2,
speed: 0.68,
size: 0.78,
radius: 0.39,
depth: 1.16,
shape: "line",
lineLength: 5.2,
lineWidth: 0.62,
jitter: 0.008,
viewport: { padding: 0.1, clip: true },
colors: ["#101c31", "#2454d4", "#0c3699"],
interaction: { mode: "rotate", strength: 0.42, smoothing: 0.07 },
});Morph between two fields
mixParticleFields() accepts built-in names or custom field functions. The mix amount can be a number or a function evaluated for every point and frame.
import { createParticleFlow, mixParticleFields } from "@scorpion7slayer/hyper-flow";
const morph = mixParticleFields(
"torus",
"rose",
({ time, index, count }) => {
const globalMix = (Math.sin(time * 0.4) + 1) / 2;
return Math.min(1, globalMix + index / count * 0.12);
},
);
createParticleFlow("#morph", {
mode: morph,
shape: "ring",
viewport: { padding: 0.12, clip: true },
});Build a transform pipeline
Pass an array directly or create a reusable transform. Each module receives the output of the previous one. Return false to remove a point.
import {
composeParticleTransforms,
createParticleFlow,
particleTransforms,
} from "@scorpion7slayer/hyper-flow";
const sculpture = composeParticleTransforms(
particleTransforms.scale(1.15, 0.72, 1),
particleTransforms.twist(0.48, 0.06),
particleTransforms.ripple(0.04, 8),
particleTransforms.limit(0.94),
);
createParticleFlow("#sculpture", {
mode: "swarm",
transform: sculpture,
});Keep particles inside a component
viewport.padding shrinks the projection area. viewport.clip confines particles, custom renderers and frame hooks to the same inset rectangle.
createParticleFlow("#card-field", {
mode: "burst",
radius: 0.46,
viewport: { padding: 0.14, clip: true },
particles: { base: 180, min: 80, max: 640 },
});Use an exact numeric particles value for deterministic captures. Use "auto" to scale density with the canvas.
Ribbon with a fading trace
createParticleFlow("#ribbon", {
mode: "ribbon",
density: 1.7,
speed: 0.78,
shape: "line",
lineLength: 7,
lineWidth: 0.5,
trail: 0.82,
blendMode: "lighter",
interaction: { mode: "repel", strength: 0.24, radius: 1.2 },
});Trail values are clamped between 0 and 0.98. Disable the trail before exporting a deterministic still frame.
Connected custom topology
Connections are not restricted to the constellation and lattice modes.
createParticleFlow("#network", {
mode: "stream",
density: 1.4,
connections: {
distance: 0.12,
opacity: 0.14,
width: 0.5,
color: "#2454d4",
maxPoints: 280,
},
});The engine connects depth-sorted neighbors within the configured screen-space distance. Lower maxPoints first when optimizing a large canvas.
Attract points into a tunnel
import { createParticleFlow, particlePresets } from "@scorpion7slayer/hyper-flow";
createParticleFlow("#portal", {
...particlePresets.portal,
origin: { x: 0.62, y: 0.5 },
scale: { x: 1.1, y: 0.82, z: 1.25 },
interaction: {
mode: "attract",
strength: 0.3,
radius: 1.1,
smoothing: 0.06,
},
});Paint a custom mark
The custom renderer receives projected coordinates. The engine has already applied rotation, perspective, depth sorting and opacity.
createParticleFlow("#marks", {
mode: "wave",
renderer(context, point, frame) {
const length = point.color === 2 ? point.renderSize * 5 : point.renderSize * 2;
const angle = frame.index * 2.399 + frame.time * 0.2;
context.strokeStyle = point.colorValue;
context.lineWidth = point.color === 2 ? 1.2 : 0.55;
context.beginPath();
context.moveTo(
point.screenX - Math.cos(angle) * length,
point.screenY - Math.sin(angle) * length,
);
context.lineTo(
point.screenX + Math.cos(angle) * length,
point.screenY + Math.sin(angle) * length,
);
context.stroke();
},
});Add frame-level drawing
Frame hooks are useful for masks, guides and overlays that should share the engine lifecycle.
createParticleFlow("#instrument", {
mode: "grid-wave",
beforeRender({ context, width, height }) {
context.strokeStyle = "rgb(36 84 212 / 0.12)";
context.strokeRect(0.5, 0.5, width - 1, height - 1);
},
afterRender({ context, width, height }) {
context.fillStyle = "rgb(16 28 49 / 0.72)";
context.fillRect(width - 3, height - 3, 2, 2);
},
});Switch a scene at runtime
update() accepts every particle option. It keeps the same canvas, observers and controller.
orb.update({
mode: "infinity",
shape: "star",
shapeOptions: { fill: false, strokeWidth: 0.7 },
parameters: { frequency: 1.25, amplitude: 0.8, twist: -0.4 },
viewport: { padding: 0.12, clip: true },
trail: 0.7,
interaction: { mode: "attract", strength: 0.28 },
});Call destroy() when the owning component unmounts.