Public
Edited
Feb 8, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function run(f0) {
const ctx = DOM.context2d(500, 500);
const canvas = ctx.canvas;
const w = canvas.width;
const pixelRatio = window.devicePixelRatio;

const f = ctx=>tf.tidy(()=>tf.reshape(f0(ctx), [-1, 2]));

let optimizer;
let knobs = f();
let pullKnobIdx = -1;
let pullPos = null;

function loss_f() {
return f(null).slice(pullKnobIdx, 1)
.sub(pullPos).square().sum().mul(0.5);
}

function render() {
if (pullKnobIdx >= 0) {
for (let i=0; i < 10; ++i) {
const {value, grads} = optimizer.computeGradients(loss_f);
optimizer.applyGradients(grads);
}
// Hack to get intermediate grads: add differentiable zeros.
const hackGrads = tf. variable(tf.zerosLike(knobs));
const knobGrads = tf.variableGrads(() => {
return f(null).add(hackGrads).slice(pullKnobIdx, 1).square().sum().mul(0.5);
}, [hackGrads]).grads[hackGrads.name];
console.log(knobGrads.dataSync());

requestAnimationFrame(render);
}

ctx.resetTransform()
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.scale(w/2, w/2);
ctx.translate(1.0, 1.0);
ctx.scale(1.0, -1.0)
ctx.lineWidth = 2.0/w*pixelRatio;
knobs.dispose();
knobs = f(ctx);
knobs.data().then(p=>{
for (let i=0; i<p.length; i += 2) {
ctx.beginPath();
ctx.arc(p[i], p[i+1], 10.0/w*pixelRatio, 0.0, Math.PI*2.0);
i==pullKnobIdx*2 ? ctx.fill() : ctx.stroke();
}
});
}
requestAnimationFrame(render);

const toNormalized = xy=>{
const w = canvas.width/pixelRatio;
const [x, y] = xy;
return [2*x/w-1.0, -2*y/w+1.0];
};

function mouseDown(e) {
pullPos = toNormalized([e.offsetX, e.offsetY]);
tf.tidy(()=>{
pullKnobIdx = knobs.sub(pullPos).square().sum(-1)
.mul(-1).argMax().dataSync()[0];
});
if (optimizer) tf.dispose(optimizer);
optimizer = tf.train.sgd(0.05);
//console.log(tf.memory());
requestAnimationFrame(render);
}

function mouseMove(e) {
pullPos = toNormalized([e.offsetX, e.offsetY]);
}

function mouseUp(e) {
pullKnobIdx = -1;
}
canvas.addEventListener('pointerdown', mouseDown);
canvas.addEventListener('pointermove', mouseMove);
canvas.addEventListener('pointerup', mouseUp);
return ctx.canvas;
}
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more