Spaces:
Running
Running
| /* Function plotter — pick an activation/curve, drag the input x, and watch z = w·x + b feed through | |
| * f(z); the curve draws itself and a tracer marks (x, f(x)). Mirrors the Manim `plot` animation. */ | |
| (function () { | |
| const k = MANIMO.kit; | |
| const FNS = { | |
| sigmoid: { f: (z) => 1 / (1 + Math.exp(-z)), tex: "σ(z) = 1 / (1 + e⁻ᶻ)", yr: [0, 1] }, | |
| tanh: { f: (z) => Math.tanh(z), tex: "tanh(z)", yr: [-1, 1] }, | |
| relu: { f: (z) => Math.max(0, z), tex: "ReLU(z) = max(0, z)", yr: [0, 4] }, | |
| decay: { f: (z) => Math.exp(-Math.max(0, z)), tex: "e⁻ᶻ (loss decay)", yr: [0, 1] }, | |
| linear: { f: (z) => z, tex: "z", yr: [-4, 4] }, | |
| }; | |
| function mount(root, concept, theme) { | |
| const p = (concept && concept.params) || {}; | |
| let name = FNS[p.shape] ? p.shape : (p.shape === "parabola" ? "relu" : "sigmoid"); | |
| let w = p.w != null ? p.w : 1, b = p.b != null ? p.b : 0, x = 0; | |
| const cv = document.createElement("canvas"); cv.width = 460; cv.height = 240; cv.className = "plot"; | |
| cv.setAttribute("role", "img"); cv.setAttribute("aria-label", "Activation function plot with a draggable input tracer"); | |
| const readout = k.el("div", { class: "readout" }); | |
| const sel = k.el("select", { class: "sel", onchange: (e) => { name = e.target.value; draw(); } }, | |
| Object.keys(FNS).map((nm) => k.el("option", { value: nm }, [nm]))); | |
| sel.value = name; | |
| const sx = slider("x", -4, 4, x, (v) => { x = v; draw(); }); | |
| const sw = slider("w", -3, 3, w, (v) => { w = v; draw(); }); | |
| const sb = slider("b", -3, 3, b, (v) => { b = v; draw(); }); | |
| function draw() { | |
| const fn = FNS[name], W = cv.width, H = cv.height, ctx = cv.getContext("2d"); | |
| const xr = [-4, 4], yr = fn.yr; | |
| const X = (xv) => (xv - xr[0]) / (xr[1] - xr[0]) * (W - 30) + 20; | |
| const Y = (yv) => H - 24 - (yv - yr[0]) / (yr[1] - yr[0]) * (H - 40); | |
| ctx.fillStyle = theme.bg; ctx.fillRect(0, 0, W, H); | |
| // axes | |
| ctx.strokeStyle = theme.muted; ctx.lineWidth = 1; ctx.beginPath(); | |
| ctx.moveTo(20, Y(Math.max(yr[0], 0))); ctx.lineTo(W - 8, Y(Math.max(yr[0], 0))); | |
| ctx.moveTo(X(0), 16); ctx.lineTo(X(0), H - 18); ctx.stroke(); | |
| // curve | |
| ctx.strokeStyle = theme.accent; ctx.lineWidth = 3; ctx.beginPath(); | |
| for (let i = 0; i <= 200; i++) { const xv = xr[0] + (xr[1] - xr[0]) * i / 200; const z = w * xv + b; const yv = fn.f(z); (i ? ctx.lineTo : ctx.moveTo).call(ctx, X(xv), Y(yv)); } | |
| ctx.stroke(); | |
| // tracer at x | |
| const z = w * x + b, y = fn.f(z); | |
| ctx.fillStyle = theme.accent2; ctx.beginPath(); ctx.arc(X(x), Y(y), 6, 0, 7); ctx.fill(); | |
| ctx.strokeStyle = theme.accent2; ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(X(x), Y(yr[0])); ctx.lineTo(X(x), Y(y)); ctx.lineTo(X(0), Y(y)); ctx.stroke(); ctx.setLineDash([]); | |
| readout.innerHTML = "z = w·x + b = " + k.fmt(w) + "·" + k.fmt(x) + " + " + k.fmt(b) + " = <b>" + k.fmt(z) + "</b> → " + fn.tex + " = <b>" + k.fmt(y) + "</b>"; | |
| } | |
| let raf = null; | |
| function stop() { if (raf) { cancelAnimationFrame(raf); raf = null; } } | |
| function play() { stop(); const t0 = performance.now(); (function tick(t) { x = -4 + 8 * (((t - t0) / 3400) % 1); sx.set(x); draw(); raf = requestAnimationFrame(tick); })(t0); } | |
| // one-shot sweep for "Play all": glide x left→right once (slowly, so the curve reads), then signal done. | |
| function playOnce(done) { | |
| stop(); const dur = 3400, t0 = performance.now(); | |
| (function tick(t) { | |
| const u = (t - t0) / dur; | |
| if (u >= 1) { x = 4; sx.set(x); draw(); raf = null; if (done) done(); return; } | |
| x = -4 + 8 * u; sx.set(x); draw(); raf = requestAnimationFrame(tick); | |
| })(t0); | |
| } | |
| root.appendChild(k.row([k.el("label", { class: "fld" }, ["function ", sel]), readout], "ctrls")); | |
| root.appendChild(cv); | |
| root.appendChild(k.row([sx.node, sw.node, sb.node], "sliders")); | |
| root.appendChild(k.row([k.button("▶ Sweep x", play), k.button("⏹ Stop", stop, "ghost")], "ctrls")); | |
| draw(); | |
| return { play: playOnce, stop, reset: () => { stop(); x = 0; sx.set(0); draw(); } }; | |
| } | |
| function slider(label, lo, hi, val, on) { | |
| const out = MANIMO.kit.el("span", { class: "sval" }, [MANIMO.kit.fmt(val)]); | |
| const inp = MANIMO.kit.el("input", { type: "range", min: lo, max: hi, step: 0.05, value: val, oninput: (e) => { out.textContent = MANIMO.kit.fmt(+e.target.value); on(+e.target.value); } }); | |
| return { node: MANIMO.kit.el("label", { class: "fld" }, [label + " ", inp, out]), set: (v) => { inp.value = v; out.textContent = MANIMO.kit.fmt(v); } }; | |
| } | |
| MANIMO.registry["plot"] = { title: "Function Plot", mount }; | |
| })(); | |