HydroComplete needed to draw treatment trains, storm sewer systems and emergency notification chains — directed networks where the flow direction carries the engineering meaning. So we wrote the engine that draws them. Every diagram on this page is live, rendered right now by the same code that runs inside the product.
These are the network shapes a water resources practice actually produces. Each one is a real model in the engine, and all of it is live: drag a node and the orthogonal router reroutes around it, click one to get resize handles, right-click for a context menu, double-click a link to rename it in place. Switch the layout to watch the algorithms rearrange the same graph, and download any of them as PNG or SVG.
Runoff from three surfaces, routed through BMPs to a detention pond and out. This is the shape HydroComplete was built around: the flow direction is the calculation order, so a layered layout is not decoration — it is the engineering read.
Who calls whom when a dam is in distress. An Emergency Action Plan requires this chart, and it is a tree by regulation — every downstream contact traces back to one initiating authority. Tree layout, top-down.
Twenty structures — inlets, junction manholes, a trunk line and an outfall. Convergent networks are where layer ordering earns its keep: without the crossing-reduction pass the same graph comes out as a cat's cradle of pipes.
Intake to distribution, with a backwash return that loops upstream. That return is a cycle, and a naive longest-path layer assignment never terminates on one — this engine bounds the relaxation and lays it out anyway.
Basins, lift stations and an interceptor feeding a treatment plant. Shown force-directed, where clusters emerge from connectivity rather than from an imposed hierarchy — useful when the question is which basins share a failure path.
Three basins discharging to one regional pond. The basin boxes are containers, not decoration: they are sized by what is inside them, they move what they hold, and collapsing one (double-click the title bar) folds its network away and re-attaches the links that crossed the boundary to the box itself. That last part is the whole point of a container in a review meeting — you argue about one basin without deleting the other two.
A drainage area decomposed into subcatchments and land cover, the structure behind a composite curve number. Hierarchy, so a tidy tree reads it best — parents centred over their children.
HydroDiagram is not a general diagramming toolkit and does not try to be one. It does one family of diagram — directed networks where flow matters — and it does that family in 245 KB with nothing else to install.
The same model lays out identically every time, force-directed included: starting positions are seeded from a hash of the node key rather than a random number generator. A diagram that shifts between runs cannot be regression-tested, and ours is — on every commit.
Real hydraulic models contain loops: a pond that discharges back upstream through an overflow, a backwash line returning to the head of the plant. Layer assignment is bounded, so a cycle produces a slightly imperfect layer instead of a frozen tab.
Layouts run left-to-right, top-down, or reversed, because a storm sewer profile reads one way and a notification chain reads another. The orthogonal router keeps pipes on the grid and steps them over each other at crossings.
Eight resize handles on the selected node, sized in screen pixels so they stay grabbable at any zoom, with the opposite edge pinned and only the dragged edges snapping to the grid. Context menus are real DOM — focusable, keyboard-dismissable, and replaceable wholesale. Labels edit in place through an input element, because text entry needs an IME, selection and clipboard that a canvas caret cannot give you.
A group has no coordinates of its own: its box is the union of what it holds, recomputed every frame, so it can never drift away from its contents. Collapse one and the links that crossed its boundary re-attach to the box rather than vanishing — which is what makes a container useful in a review meeting instead of just tidy. Layouts are compound: each container is laid out among its own members first, then stands in as a single box at the level above, so switching to Tree or Force-directed does not turn a grouped model into confetti.
Pinch to zoom about the midpoint between the fingers, long press for the context menu a tablet has no right button to reach, double tap to edit a label. Resize handles grow to a finger-sized target the moment a touch pointer appears, and the band that starts a link narrows — an accidental link is worse than an accidental move. Embedded diagrams can leave vertical swipes to the page, so a diagram in a long document does not trap the scroll.
Tab walks the nodes in reading order and pans each one into view, releasing focus at the end of the list rather than trapping it. Arrows nudge the selection by one grid cell, or one pixel with Shift, and pan the view when nothing is selected. Copy, cut, paste and duplicate carry the links whose both ends came along, so a pasted sub-network is still a network — and the payload goes to the system clipboard as text, so it survives the trip to another tab.
PNG at any scale and real vector SVG, both of the whole document rather than whatever is in the viewport — because the deliverable is a report, not a screenshot. Exports render through the same code the screen uses, so there is no second renderer to drift, and selection highlighting is deliberately left out: a node that happened to be selected must not come out highlighted in a submittal.
Written in-house, no third-party diagramming code copied, adapted or derived. That removes a per-seat cost from the product and a third-party licence from anyone's diligence checklist.
Synthetic convergent drainage networks — many sources funnelling through junctions to one outfall, which is the shape this engine is for — in a 1200×800 viewport, headless Chromium. Median of five runs, because single samples on a laptop swing by three times.
| Nodes | Links | Layout | First draw | Redraw | Hit test |
|---|---|---|---|---|---|
| 50 | 56 | 0.2 ms | 9 ms | 0.4 ms | 0.006 ms |
| 100 | 110 | 0.2 ms | 24 ms | 0.6 ms | 0.011 ms |
| 250 | 276 | 0.6 ms | 100 ms | 1.5 ms | 0.019 ms |
| 500 | 550 | 1.3 ms | 20 ms | 3.1 ms | 0.034 ms |
| 1000 | 1100 | 1.6 ms | 62 ms | 6.2 ms | 0.073 ms |
Read this honestly. Redraw holds 60 fps to a
thousand nodes because routes and their crossing hops are cached and
recomputed only when geometry changes — a pan or a zoom cannot alter an
orthogonal route. First draw is the one-time routing cost, and it is not
linear: it depends on how much clutter each link has to find its way
around, which is why 250 dense nodes cost more than 500 sparser ones.
Run it yourself with
build-tools/probe-hydrodiagram-perf.mjs.
One file, no dependencies, no build step. It runs the moment you load it, with an evaluation mark in the corner of every diagram until a key is installed — so you can build the whole integration, decide it is right, and buy last.
↓ Download hydrodiagram.js v0.1.0 · ~240 KB uncompressed · pin this version
<script src="hydrodiagram.js"></script>
<canvas id="c" width="800" height="400"></canvas>
<script>
const d = new HydroDiagram.Diagram('c');
d.model = new HydroDiagram.Model(
[ { key: 'roof', text: 'Roof drainage' },
{ key: 'bio', text: 'Bioretention' },
{ key: 'pond', text: 'Detention pond' } ],
[ { from: 'roof', to: 'bio' },
{ from: 'bio', to: 'pond' } ]
);
d.layout('layered');
</script>
That is a working diagram — draggable, routable, exportable. There is no template language and no panel model to learn first.
The bundle assigns window.go and implements the surface
an integration actually touches: go.GraphObject.make,
go.Diagram, go.GraphLinksModel,
commandHandler (copy, cut, paste, group, ungroup,
collapse), makeImageData and makeSvg.
go.Diagram.licenseKey = key works as-is.
That is how HydroComplete moved: the application code was not rewritten. Swap the script tag, keep the diagram code, and the per-developer licence leaves your cost base and your diligence checklist.
It is not the whole of GoJS, and you should find that out in thirty
seconds rather than on day two. Run the checker against your source
and it reports what is implemented, what is missing, and — the part
that actually costs people time — what HydroDiagram accepts and then
silently ignores, like routing and table panels.
node gojs-compat-check.cjs ./src
↓ Download the compatibility checker Node, no dependencies, no network. It reads your code, never runs it.
We ran it over all 156 GoJS sample applications and published the result, including the 80% that would not port cleanly: what actually breaks →
HydroDiagram runs unlicensed with an evaluation mark in the corner of every diagram. A key removes it. Keys are bound to the domains they were issued for, carry an optional expiry, and name the licensee.
HydroDiagram.setLicense(key), or
go.Diagram.licenseKey = key if you are moving from a
library that used that form — same call, so an existing integration
does not have to move it. licenseStatus() tells you why
a key was refused rather than failing silently.
This is client-side, and the library ships as readable JavaScript, so a determined person can remove the check. We are not going to pretend otherwise. What the domain binding does buy is that a key lifted out of one site's bundle is worthless on another, which is the realistic leak.
example.com and
*.example.comlocalhost and 127.0.0.1 included, so development
is never watermarkedPayment is handled by Stripe; we never see the card. Bought it for the wrong domain? Reply to the receipt and we will reissue it. Licence terms.
Bound to this domain. It does not expire.
…
Install it once, before you create a diagram:
HydroDiagram.setLicense(key);
A copy is on its way to your inbox. HydroDiagram.licenseStatus()
will tell you why a key was refused, if it ever is.
What it does not do. Swimlanes, org-chart styling, free drawing tools, an overview/minimap, link routing styles beyond orthogonal, and the general-purpose panel model of a full diagramming suite are all out of scope today. If you need a general-purpose diagram library, use one — this is a purpose-built engine for infrastructure flow networks, and that focus is the point.