Public
Edited
Aug 28, 2023
17 stars
Line-Sweep Ambient Occlusion with WebGPUSpring TrainingLine Sweep Ambient Occlusion in JavaScriptLine Sweep Terrain LightingSpeed Climbing World Record ProgressionWebGPU ShaderHydraulic Erosion SimulationHow Does Mapbox Raster Colorization Work?Arc Length of a Quadratic Bézier SplineMagnetic PendulumTracing Lamb Wave Modes in the Complex PlaneMissing Fundamental IllusionSliced Optimal TransportLine Integral ConvolutionShanks TransformationUeda's AttractorCubic basis vs. Hermite interpolationBicubic Texture Interpolation using Linear FilteringFactor-of-Two Lanczos Image ResamplingRendering the Aperiodic Monotileeqn [WIP]
SDF Points with regl
Knocking Down the Gates with our Friend JacobiFast Generalized Winding Numbers in 2DHTML+CSS Periodic Three-Body OrbitsClifford and de Jong AttractorsStrange Attractors on the GPU, Part 1: ImplementationStrange Attractors on the GPU, Part 2: Fun!Lawson's Klein BottleInteractive Multi-scale Turing PatternsComputing π with the Bailey-Borwein-Plouffe FormulaThe Double Pendulum MapMalkus WaterwheelRegister Allocation and the k-Coloring ProblemMultiscale Turing Patterns in WebGLSelecting the Right Opacity for 2D Point CloudsKuramoto-Sivashinsky Equation in 2DAdaptive Contouring in Fragment ShadersComplex function plotterGPU Voronoi Diagrams using the Jump Flooding AlgorithmBaker's MapHello, g9Dispersion in Water Surface WavesFake Transparency for 3D SurfacesUniformly Distributed Points on a SphereGPU BoidsGrouping Points with Principal Component AnalysisDomain Coloring for Complex FunctionsDrawing indexed mesh data as screen-space normals without duplicating dataFinding Roots in the Complex PlanePeriodic Planar Three-Body Orbits2D (Non-physical) N-body Gravity with Poisson's EquationHalf-Precision Floating-Point, VisualizedIntegers in Single-Precision Floating-PointDomain Coloring with Adaptive ContouringInstanced WebGL CirclesDouble Compound Pendulums3D Reaction-DiffusionMathematical Easter Egg ColoringToiletpaperfullerenes and Charmin Nanotubes
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof sdfData = {
// Compute the SDF
const sdf = pathSdf(svg, { width: sdfDimension, height: sdfDimension });

// Convert single-channel float to RGBA uint8
const data = new Uint8ClampedArray(sdf.length * 4);
for (let i = 0; i < sdf.length; i++) {
data[4 * i + 0] = sdf[i] * 255;
data[4 * i + 1] = sdf[i] * 255;
data[4 * i + 2] = sdf[i] * 255;
data[4 * i + 3] = 255;
}

// Construct a preview
const ctx = DOM.context2d(sdfDimension, sdfDimension, 1);
const imgData = ctx.getImageData(0, 0, sdfDimension, sdfDimension);
imgData.data.set(data);
ctx.putImageData(imgData, 0, 0);

// Assign the output to the cell value
ctx.canvas.value = data;

return ctx.canvas;
}
Insert cell
pathSdf = (await import("https://cdn.skypack.dev/svg-path-sdf")).default
Insert cell
sdf = {
const texture = regl.texture({
data: sdfData,
min: "linear",
mag: "linear",
width: sdfDimension,
height: sdfDimension
});
invalidation.then(() => texture.destroy());
return texture;
}
Insert cell
Insert cell
Insert cell
Insert cell
update = {
function update() {
regl.poll();
drawPoints({
sdf,
colorscale,
...pointProps
});
}
update();
return update;
}
Insert cell
Insert cell
drawPoints = regl({
vert: `
precision highp float;
attribute float index;
uniform float pointSize, n;
uniform sampler2D colorscale;
uniform vec2 res;
varying vec3 color;
const float pi = ${Math.PI};
void main () {
// Make up a position
const float phi = ${Math.PI * (3 - Math.sqrt(5))};
float r = sqrt(index / (n - 1.0)) * (length(res) / res.x) * 0.707;
float theta = index * phi * (r < 1e-8 ? 0.0 : 1.0);
gl_Position = vec4(0.9 * vec2(cos(theta), sin(theta) * res.x / res.y) * r * 1.57, 0, 1);

// Sample up a color (for efficiency, in the vert shader, then pass to the fragment shader)
color = texture2D(colorscale, vec2(fract(theta / pi), 0.5)).rgb;

gl_PointSize = pointSize;
}`,
frag: `
precision highp float;
uniform sampler2D sdf;
uniform float pointSize, borderWidth, aaWidth, opacity;
uniform vec3 borderColor;
varying vec3 color;

float linearstep(float edge0, float edge1, float x) {
return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
}

void main () {
// 0.5 corresponds to the shape, so compute an SDF relative to that value
float sdf = (texture2D(sdf, gl_PointCoord.xy).r - 0.5) * pointSize * 0.5;

float alpha = 0.0 + linearstep(-borderWidth * 2.0 - aaWidth, -borderWidth * 2.0 + aaWidth, sdf) * opacity;
vec3 col = borderWidth == 0.0
? color
: mix(borderColor, color, linearstep(-aaWidth, aaWidth, sdf));

if (alpha == 0.0) discard;

// Use pre-multiplied alpha to get the blending correct
gl_FragColor = vec4(vec3(col), 1) * alpha;
}`,
attributes: {
index: new Array(MAX_POINTS).fill(0).map((_, i) => i)
},
blend: {
enable: true,
func: {
srcRGB: 1,
srcAlpha: 1,
dstRGB: "one minus src alpha",
dstAlpha: "one minus src alpha"
}
},
depth: { enable: false },
uniforms: {
res: (ctx) => [ctx.viewportWidth, ctx.viewportHeight],
n: regl.prop("count"),
sdf: regl.prop("sdf"),
// Double the size since the SDF is relative to the 0.5 value
pointSize: (ctx, props) => ctx.pixelRatio * props.pointSize * 2.0,
borderWidth: (ctx, props) =>
ctx.pixelRatio *
Math.min(
props.borderWidth * 0.5,
// Limit the border size since the buffer around the SDF is only so big
(props.pointSize - (2.0 * props.aaWidth) / ctx.pixelRatio) * 0.25
),
colorscale: regl.prop("colorscale.texture"),
aaWidth: regl.prop("aaWidth"),
opacity: regl.prop("opacity"),
borderColor: (ctx, props) => {
const c = d3.rgb(props.borderColor);
return [c.r / 255, c.g / 255, c.b / 255];
}
},
primitive: "point",
count: regl.prop("count")
})
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