Published
Edited
Aug 1, 2022
Importers
5 stars
Also listed in…
Image
Interpolation
Insert cell
Insert cell
Insert cell
function heatmap(
V, // values as a flat array or an array of arrays
{
_V = typeof V.flat === "function" ? Float64Array.from(V.flat()) : V, // values as a flat array
color = d3.interpolateGreys, // default color interpolator
extent = d3.extent(_V), // default extent; values outside the extent are transparent, or clamped
domain = extent,
clamp = false,
dither = true, // a bit of dithering to avoids visible steps
width = V.width || (_V.length !== V.length ? V[0].length : null) || 960,
height = _V.length / width,
contours = false,
pixelated = true,
upscale = 1, // TODO: better API?
context
} = {}
) {
context = context !== undefined ? context : DOM.context2d(width, height, 1);
const im = context.getImageData(0, 0, width, height);
const sl = d3.scaleLinear().domain(domain).range([0, 255]).clamp(clamp);

const c = d3.quantize(color, 256).map((x) => d3.rgb(x));
for (let i = 0; i < _V.length; i++) {
const s = sl(_V[i]),
s0 = Math.floor(s);
const v = dither && Math.random() < s - s0 ? c[s0 + 1] : c[s0];
if (!v) continue;
im.data[4 * i] = v.r;
im.data[4 * i + 1] = v.g;
im.data[4 * i + 2] = v.b;
im.data[4 * i + 3] = 255;
}

context.putImageData(im, 0, 0);
context.canvas.value = V; // compatible with viewof

if (contours) {
const path = d3.geoPath().context(context);
context.beginPath();
for (const c of d3.contours().size([width, height])(
Float64Array.from(_V, (d) => d || 0)
))
path(c);
context.strokeStyle = typeof contours === "string" ? contours : "white";
context.stroke();
}

return Object.assign(context.canvas, {
style: `width:${width * upscale}px;height:${height * upscale}px;${
pixelated ? " image-rendering: pixelated;" : ""
}`
});
}
Insert cell
Insert cell
heatmap(
d3
.range(120)
.map((i) => d3.range(120).map((j) => Math.sin(i / 19) * Math.sin(j / 19))),
{ upscale: 2, pixelated: false }
)
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more