JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.4.4/d3.min.js"></script>
<label>Image URL: <input id="input" value="https://picsum.photos/id/870/2900/4334"></label>
<div id=output></div>
<div id=preview></div>

CSS

label {
  display: block;
  padding: 10px;
  box-sizing: border-box;
}
label input {
  width: 100%;
}
#preview img {
  max-width: 400px;
  max-height: 400px;
}

JavaScript

const input = document.getElementById('input');
const output = document.getElementById('output');
const preview = document.getElementById('preview');

input.onchange = async () => {
  clear(output);
  clear(preview);
  // Load the image.
	const img = new Image();
  img.crossOrigin = 'anonymous';
  img.src = input.value;
  preview.appendChild(img);
  await img.decode();
  
  // Create and inject the histogram.
  output.appendChild(histogramVisualization(img));
};
if(input.value) input.onchange();

function clear(element) {
	element.innerHTML = '';
}

function histogramVisualization(img, {width = 400} = {}) {
  const height = width / 3;
  const margin = 2;
  const histogram = getHistogram(getImageData(img).data);

  const x = d3
  .scaleLinear()
  .domain([0, 256])
  .range([0 + margin, width - margin]);
  const y = d3
  .scaleLinear()
  .domain([0, d3.max(histogram.map(x => d3.max(x.data)))])
  .range([height - margin, 0 + margin]);

  const area = d3
  .area()
  .x((_, i) => x(i))
  .y0(y(0))
  .y1((d, _) => y(d))
  .curve(d3.curveStepAfter);

  const svg = d3
  .create("svg")
  .attr("width", width)
  .attr("height", height)
  .attr("class", "histogram")
  .style("background-color", "#333")
  .style("isolation", "isolate");

  for (const channel of histogram) {
    svg
      .append("path")
      .attr("class", channel.name)
    // duplicate the last data point so that the right-most step is drawn correctly
      .attr("d", area([...channel.data, channel.data.slice(-1)]))
      .attr("fill", channel.color)
      .attr("stroke", channel.color)
      .style("mix-blend-mode", "screen");
  }

  return svg.node();
}

function getHistogram(data) {
  // dividing the histogram into channels like this is @lemonnish's idea
  const histogram = [
    { name: "red", color: "#ff0000", data: new Uint32Array(256) },
    { name: "green", color: "#00ff00", data: new Uint32Array(256) },
    { name: "blue", color: "#0000ff", data: new Uint32Array(256) }
  ];

  for (let i = 0; i <...