23 comments

[ 4.7 ms ] story [ 46.5 ms ] thread
I feel like D3 ought to be the for-computer substrate for libraries that are actually for humans.

I suppose it matters less now in the LLM age.

More granular control, more verbosity.

I am still proud of the D3 gadget I made about 8 years back as a green web dev. Couldn't have made it any other way, not sure if I could with any other library today. Wouldn't want to do it again, though, unless I was a dedicated front-end guy.

This is why I've always found it weird that people consider D3 to be a charting tool. Yes, people have used it to build a lot of charts, but it's really just a streaming data processing tool. It doesn't provide anything specific to charting[0]. All of that part, you're still left to figure out on your own.

[0] At least in the core, I'm not too familiar with the full ecosystem and what is considered official in terms of plugins. Everytime I've tried to use it, I've not found the documentation leading me to using anything more specifically oriented towards charting.

I'd say because JavaScript is insufficiently expressive. No macros, no arrow (pipe) operator, and the premier way of writing it is 4x as verbose than what I'd consider reasonable. D3 is a great library though.
(comment deleted)
recently i've been having a lot of success with working with d3 + solid.js. I use d3 as the data processing layer, and solid for actually rendering svgs from the data. the combination is lovely, you get all the power of d3, while the parts that usually end up verbose are written succinctly in jsx. and it's a lot less pain than doing it in react, because the mental models of solid/d3 feel much more aligned
Note: the example is a misconception and not what's meant by "binding to data." In D3, binding to data refers to using the `.data()` method to supply an object (typically an array) which you can then use in a function callback in the accessors, so like `.attr('x1', d => /* access individual array item here */)`. This allows you to easily bind a dataset to a graphical representation and use its attributes to inform the properties of the visualization.

I'd also argue that D3 is no more verbose than vanilla JS (at least for this example). What's the alternative for creating a line in SVG?

    const line = document.createElementNS('http://www.w3.org/2000/svg', 'line')
    line.setAttribute('x1', ...)
    line.setAttribute('y1', ...)
    line.setAttribute('x2', ...)
    line.setAttribute('y2', ...)
    // etc
    document.querySelector('svg').appendChild(line)
D3 is painful, we don't have to appeal to authority to admit that.

Mike Bostock is an interesting person, and a case study in why we don't design languages from a single person's genius.

I love D3, but its a library not a language.
Because the alternative is big config files or a declarative DSL. Builder pattern works really well here to keep things simple.
Man, my first startup in 2010 used protovis, the charting library Mike Bostock built before deciding d3 was the better approach. It was rough to have an 8 month old startup with a core piece of tech that suddenly stopped improving.

My main takeaway from so much of this is that "just a chart" is one of the biggest sources of hidden complexity in displaying useful information to people. It's right up there with "a simple web form" and "a web page with some simple interactivity."

Everybody has a wildly different idea of what good looks like. Defaults will never be right. Personal and global taste changes annually. We clown react (rightly) for constantly reinventing the same 4 wheels, but customers gleefully use new stuff all the time.

It's kind of amazing that d3 has been so durable in the frontend world. It really is a wrapper over a pretty solid approach. And yeah, that approach is complex, but that's the reality of visualization. It's hard to imagine another one that's that good.

Verbosity is readability. I'd wager some of the terser libraries take more effort picking up again after leaving them for a while.

Whereas once the D3 training wheels come off, its muscle memory is hard to shed.

Love all D3 content, but I'd add that the data binding just to create svg is not the real reason - after all, you can do this declaratively using most modern frameworks by directly iterating over the data and returning a positioned element (fwiw this is how I prefer to use it today). The reason is because of the complexity within d3 selection, namely the enter/update/exit + transition capabilities: https://www.d3indepth.com/enterexit/

Another thing worth mentioning that newcomers seem to take for granted - the margin boilerplate required for correct positioning (see https://observablehq.com/@d3/margin-convention).

I could not get over the fact that 0,0 in d3 is the top left corner instead of the bottom left.

Why??

Everything in real life uses bottom left for 0,0! Probably because the first EGA/VGA accelerators worked this way to save one instruction in the most common use case and things never change....

First of all, shocked people still use d3. Hasn't there been something better? It's pretty ancient by javascript standards. Do people still use jquery too? Haha... been about a decade since I touched this stuff!

Second of all, isn't it ungodly slow? I get that it can draw a few boxes nicely, and maybe shuffle them around, but I had to write my own engine using html canvas because d3 couldn't get svg to flow properly if I had thousands of pixels in my image.

Honestly, if you're going to go through the trouble of understanding d3, I would just write your own javascript canvas to animate things.

Vue is about as ancient yet people still use that. Python is even older.
I tried D3 a few times, with some success, but eventually abandoned it. The mental model it requires is so different from other charting libraries it seems too difficult for my brain to grasp for any period of time.

Also, I've come to really dislike libraries that use this kind of `a.b().c()` chained form:

    boxplotContainer
    .append("line")
      .attr("x1", xScale(gender) - boxplotWidth/2)                          
        .attr("x2", xScale(gender) + boxplotWidth/2)
I find it hard to reason about. What is the return value of boxplotContainer.append("line")? How do I debug it? What values can I inspect?

That, and I really never got comfortable with the `.enter()` and `.exit()` and `.join()` concepts. It's so abstract.