Public
Edited
Jan 4, 2023
Insert cell
Insert cell
[...Array(100)].map((_, i) => i * i)
Insert cell
GPU = require("gpu.js@2")
Insert cell
gpu = new GPU.GPU()
Insert cell
width = 800
Insert cell
height = 600
Insert cell
velocityScaleFactor = 8
Insert cell
velocityWidth = Math.ceil(width / velocityScaleFactor)
Insert cell
velocityHeight = Math.ceil(height / velocityScaleFactor)
Insert cell
advection = gpu.createKernel(function(state, velocity) {
const v = velocity[this.thread.y][this.thread.x];
return state[this.thread.y - v[1] / this.constants.height][this.thread.x - v[0] / this.constants.width];
})
.setArgumentTypes(["Array2D(2)", "Array2D(2)"])
.setConstants({ width, height })
.setOutput([velocityWidth, velocityHeight])
Insert cell
divergence2D = gpu.createKernel(function(field) {
const n = field[this.thread.y + 1][this.thread.x];
const s = field[this.thread.y - 1][this.thread.x];
const e = field[this.thread.y][this.thread.x + 1];
const w = field[this.thread.y][this.thread.x - 1];
return 0.5 * (e[0] - w[0] + n[1] - s[1]);
})
.setArgumentTypes(["Array2D(2)"])
.setConstants({ width, height })
.setOutput([velocityWidth, velocityHeight])
Insert cell
divergence2D(makeVelocityStateArray());
Insert cell
jacobi = gpu.createKernel(function(state, divergence) {
const n = state[this.thread.y + 1][this.thread.x];
const s = state[this.thread.y - 1][this.thread.x];
const e = state[this.thread.y][this.thread.x + 1];
const w = state[this.thread.y][this.thread.x - 1];
const div = divergence[this.thread.y][this.thread.x];
return 0.25 * (n + s + e + w - div);
})
.setArgumentTypes(["Array", "Array"])
.setConstants({ width, height })
.setOutput([velocityWidth, velocityHeight])
Insert cell
gradientSubtraction = gpu.createKernel(function(sfield, vfield) {
const n = sfield[this.thread.y + 1][this.thread.x];
const s = sfield[this.thread.y - 1][this.thread.x];
const e = sfield[this.thread.y][this.thread.x + 1];
const w = sfield[this.thread.y][this.thread.x - 1];
const vec = vfield[this.thread.y][this.thread.x];
return [vec[0] - 0.5 * (e - w), vec[1] - 0.5 * (n - s)];
})
.setArgumentTypes(["Array", "Array2D(2)"])
.setOutput([velocityWidth, velocityHeight])
Insert cell
makeVelocityStateArray = gpu.createKernel(function() {
return [
10 * this.thread.x / this.constants.width,
10 * this.thread.y / this.constants.height,
];
})
.setConstants({ width, height })
.setOutput([velocityWidth, velocityHeight])
Insert cell
makePressureStateArray = gpu.createKernel(function() {
return 0;
})
.setConstants({ width, height })
.setOutput([velocityWidth, velocityHeight])
Insert cell
velocity = {
const NUM_JACOBI_STEPS = 3;
let velocityState = makeVelocityStateArray();
let pressureState = makePressureStateArray();

while (true) {
velocityState = advection(velocityState, velocityState);
let divergenceState = divergence2D(velocityState);
for (let i = 0; i < NUM_JACOBI_STEPS; i++) {
pressureState = jacobi(pressureState, divergenceState);
}
velocityState = gradientSubtraction(pressureState, velocityState);
yield velocityState;
}
}
Insert cell
render = gpu.createKernel(function(velocity) {
// const v = velocity[0][0];
const v = velocity[
Math.floor(this.thread.y * this.constants.velocityHeight / this.constants.height)
][Math.floor(this.thread.x * this.constants.velocityWidth / this.constants.width)];
// this.color(v[0] < 0 ? 1 : 0, 0, 0);
// const val = velocity[
// this.thread.y * this.constants.velocityHeight / this.constants.height
// ][this.thread.x * this.constants.velocityWidth / this.constants.width];
this.color(0.5 + v[0], 0.5 + v[1], 0);
})
.setArgumentTypes(["Array2D(2)"])
.setConstants({ width, height, velocityWidth, velocityHeight })
.setOutput([width, height])
.setGraphical(true)
Insert cell
{
render(velocity);
return render.canvas
}
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