D3 Is a Way to Compose a Visualization
D3 is an open-source JavaScript library for visualizing data, but that description leaves out the decision that defines it. D3 is deliberately low-level and built on web standards, so it does not primarily offer finished charts with a fixed appearance and behavior. It offers mechanisms from which an author can construct a dynamic, data-driven graphic. This series examines D3 through that compositional design: how data is prepared, attached to elements, mapped into visual variables, turned into geometry, rendered, and made interactive—and what the author must still decide at each boundary.
That focus matters because a visualization is not just a picture produced at the end of a script. It is a chain of choices. A table may be grouped before it is drawn; a record may need a stable identity before it can be updated; a number may become a position, a color, or a size; a layout may expose one relationship while hiding another; and an interaction may change what is recomputed and what remains stable. D3 keeps those choices visible. Its flexibility is therefore also a request for judgment.
D3 is not a chart menu
A chart menu asks a reader or developer to choose a named form: a bar chart, a line chart, a map, or a network. That can be convenient, but it tends to bundle several decisions together. The chart may decide how data is grouped, how elements are identified, which visual variables are used, how the geometry is generated, and how updates behave. If the problem fits the bundle, the result can be quick. If it does not, the hidden decisions become constraints.
D3 starts from the other direction. Its repository describes a low-level approach based on web standards and emphasizes flexibility in authoring dynamic, data-driven graphics. In practice, that means D3 supplies operations and interfaces rather than a single policy for what a chart should be. A selection can be joined to data. A scale can map an abstract value into a visual range. A shape generator can turn accessors into geometry. A browser renderer can display that geometry. A behavior can respond to input. Each mechanism has a more limited responsibility than a finished chart, and the author connects them around a question.
This does not mean that D3 cannot produce familiar charts. Lines, areas, arcs, symbols, axes, maps, hierarchies, and force-directed graphs are all supported. The difference is where the design remains visible. A line chart is not a sealed object; it is a relationship among data, scales, a line generator, marks, labels, and perhaps interaction. D3 gives the author control over those relationships so that a conventional form can be adapted—or a less conventional form can be built when the data requires it.
A visualization is a chain of decisions
The most useful starting model is a pipeline. The stages are not a mandatory recipe, and real applications may repeat or skip them, but they describe the responsibilities that D3 makes explicit.
The first decision concerns the data itself. D3 includes tools for parsing delimiter-separated files, fetching common formats, grouping records, rolling values up, filtering arrays, computing summaries, and generating bins or ticks. These operations do not yet draw anything. They decide what the visual system will treat as an observation, a group, an interval, or a summary. If the transformation removes a distinction that matters to the question, a later visual encoding cannot restore it.
The next decision is identity. D3 selections bind data to elements in the document, and bound data remains available on those elements for later operations. When new data arrives, D3 can match it to existing elements by position or by a key supplied by the author. That distinction determines whether a changing value is treated as the continuing life of an existing mark or as a new mark replacing an old one. The join therefore carries meaning: it is not merely a way to reduce typing.
After identity comes encoding. A scale maps a dimension of abstract data to a visual representation. The representation might be horizontal position, vertical position, color, stroke width, or symbol size. Axes and formats make the mapping legible to a reader; color schemes and interpolation shape how differences and transitions are perceived. Geometry then gives the encoded values form. A shape generator can use accessors to construct a line, area, arc, or other mark, while a layout can compute positions for a more structured display.
Finally, the geometry must reach a renderer and respond to the world outside the graphic. D3 supports output for SVG and Canvas, and it supplies behaviors for events, brushing, dragging, zooming, transitions, and related feedback. The result is not a one-way path from data to pixels. An interaction may change a selection, recompute a scale or layout, and render a new view. The same composition that creates the first picture also determines how that picture changes.
What composition buys—and what it costs
D3's package reflects this pipeline. The default d3 package brings together focused libraries for arrays, selections, scales, shapes, interaction, layouts, geography, and other concerns. The project documentation says these modules are designed to work together but can also be used independently. That arrangement lets an author use the broad default build or select narrower capabilities for a particular application. It also makes the boundaries inspectable: a scale is not a selection, a shape generator is not a data loader, and a brush is not an application state store.
Those boundaries create useful choices. A developer can use D3's scales and shapes while leaving document updates to another system. They can use selections for a small interactive graphic without adopting every analytical facility. They can combine a hierarchy with a treemap, or use a force simulation whose ticks feed an SVG or Canvas renderer. Modularity also allows specialized parts of the project to evolve independently. The D3 change history presents this as a move toward many small libraries, separate repositories, and independent release cycles rather than one undifferentiated library.
But composition is not free. The author has to understand how the pieces exchange values and state. A custom combination can make responsibilities clear, yet it can also expose version, namespace, and coordination work. D3's move toward ES modules, native collections, iterables, the Fetch API, and direct event passing aligns it with modern JavaScript and browser conventions, but the same changes required users of older versions to migrate code. A modular system gives more control partly because it refuses to hide every decision.
That is why a list of D3 modules is a poor substitute for an operating model. Knowing that a scale, a selection, or a brush exists does not explain what crosses its boundary. The important question is: what information enters this mechanism, what does it compute or change, and who owns the next decision? D3 becomes easier to reason about when each module is placed in that flow.
The author's side of the boundary
D3 provides mechanisms, not a guarantee that a visual explanation is truthful or useful. The author decides how to aggregate the data, what counts as the same entity across updates, which variable receives the strongest visual channel, which layout or projection fits the question, and whether SVG or Canvas is appropriate for the rendering and interaction needs. The author also decides how a reader will understand labels, colors, motion, and changes in state.
The repository itself offers a concrete warning about this responsibility. Its change notes describe the removal of an older set of categorical color schemes because grouped hues could falsely imply relationships and relative lightness could imply order. A built-in palette is therefore not automatically a neutral choice. The same principle applies to layout. A hierarchy can make parent-child structure visible; a force simulation can arrange nodes through constraints; a map projection can preserve some geographic properties while distorting others. The mechanism works, but the visual claim still depends on the choice around it.
The boundary is also practical. The source documents SVG and Canvas support, but it does not establish that one renderer is always faster or better. It documents data joins, but a key function is not automatically the right definition of identity. It documents interaction behaviors, but a brush or zoom does not decide what a user's action means for the rest of the application. Nor does the repository's mechanism documentation amount to a universal guarantee of accessibility, performance, or comprehension.
A useful way to approach D3 is therefore to ask four questions in order: What is the visual question? What data and identity must remain intact? Which mapping, geometry, and renderer make the intended relationship visible? What will change when the reader interacts? These questions do not replace D3's APIs. They give those APIs a place in a coherent design.
D3's distinctive value is that it lets an author answer them explicitly. Its low-level character is not an absence of design; it is an invitation to locate design in the open. The next step is to look at the architecture that makes this possible: how D3's umbrella package and focused modules cooperate without becoming a single opaque chart system.