Motion & curves
These controls edit plain config objects — a spring's physical parameters, a CSS
easing curve, a function of x — that feed straight into your own animation
code. All three are lazy-loaded heavy controls.
Spring
{ type: "spring" } is a stiffness / damping / mass tuner
with a live settle-curve preview. The param is the plain
{ stiffness, damping, mass } object — here it drives a tiny integrator.
Soften the damping, then send the ball.
const ball = target.querySelector(".spr-ball");
let x = 0, vel = 0, dest = 0, raf = 0;
const panel = tweaks("Spring", {
motion: { type: "spring", stiffness: 220, damping: 18, mass: 1 },
send: { type: "button", label: "Send it", action: () => { dest = dest ? 0 : 1; go(); } },
});
mount.append(panel.el);
const go = () => {
cancelAnimationFrame(raf);
const tick = () => {
const { stiffness, damping, mass } = panel.params.motion;
vel += ((-stiffness * (x - dest) - damping * vel) / mass) / 60;
x += vel / 60;
ball.style.left = `calc(8px + ${x} * (100% - 48px))`;
if (Math.abs(vel) + Math.abs(x - dest) > 0.0005) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
};mount is the panel's slot inside the stage; target is the demo surface it controls. In your own page you'd just document.body.append(panel.el).
Cubic bézier
A CSS easing editor — drag the two handles, or type into the
X1/Y1/X2/Y2 fields. The param is the four-number array, ready for
cubic-bezier(…) anywhere CSS takes a timing function. The dot below
ping-pongs on an infinite animation; the curve is applied live.
const dot = target.querySelector(".bez-dot");
const panel = tweaks("Easing", {
curve: { type: "cubicbezier", value: [0.25, 0.1, 0.25, 1] },
seconds: [1.2, 0.2, 3, 0.1],
});
mount.append(panel.el);
const apply = (p) => {
dot.style.animationTimingFunction = `cubic-bezier(${p.curve.join(", ")})`;
dot.style.animationDuration = `${p.seconds}s`;
};
panel.on(apply);
panel.ready.then(() => apply(panel.params));Plot
An expression grapher with a safe evaluator — no eval, just a
small parser over x, the usual math functions and constants. The
param is the expression string itself; type into the field to regraph. Pass
fn instead to graph one of your own functions (read-only), and
xMin/xMax/yMin/yMax/samples
to frame it.
params.wave = "sin(x) * exp(-x / 6)"const readout = target.querySelector(".plt-readout");
const panel = tweaks("Plot", {
wave: { type: "plot", expr: "sin(x) * exp(-x / 6)", xMin: 0, xMax: 12 },
});
mount.append(panel.el);
const apply = (p) => {
readout.textContent = `params.wave = ${JSON.stringify(p.wave)}`;
};
panel.on(apply);
panel.ready.then(() => apply(panel.params));