# People ask for a convenient pan/zoom/search viewer

**URL:** https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768
**Category:** d3-graphviz
**Created:** [August 11, 2021, 3:31pm UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768 "2021-08-11T15:31:15Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![scnorth](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/scnorth/32/89_2.png) [@scnorth](https://forum.graphviz.org/u/scnorth)
#### Post date: [August 11, 2021, 3:31pm UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/1 "2021-08-11T15:31:15Z")

</div>

These things already exist, but with external tools. I wonder if it would be reasonable to enhance the SVG code generator possibly based on d3-graphviz in graphviz to would emit a standalone viewable graph?

---

<div class="post-metadata">

### Author: ![mark](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/mark/32/84_2.png) [@mark](https://forum.graphviz.org/u/mark)
#### Post date: [August 11, 2021, 9:51pm UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/2 "2021-08-11T21:51:39Z")

</div>

Fwiw I pan/zoom/search SVGs by opening them in a browser and using scrollbars, ctrl/+, and ctrl/f. It’s admittedly not a smooth google-maps-style experience but it works today.

I think something that output some JS to make the experience smoother (into the svg or into an html output) would be a reasonable feature request.

---

<div class="post-metadata">

### Author: ![mark](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/mark/32/84_2.png) [@mark](https://forum.graphviz.org/u/mark)
#### Post date: [August 11, 2021, 10:39pm UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/3 "2021-08-11T22:39:31Z")

</div>

As some other prior art, pprof has a zoomable pannable graphviz viewer, see some images of it here: [https://www.goodwith.tech/blog/go-pprof](https://www.goodwith.tech/blog/go-pprof) (I can’t find a live example to share)

---

<div class="post-metadata">

### Author: ![smattr](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/smattr/32/85_2.png) [@smattr](https://forum.graphviz.org/u/smattr)
#### Post date: [August 12, 2021, 12:19am UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/4 "2021-08-12T00:19:46Z")

</div>

I am curious to see what this would look like, but I’m pessimistic that we could do anything better than what browsers do. Zooming SVGs in a modern browser is surprisingly pleasant these days.

---

<div class="post-metadata">

### Author: ![ronman](https://avatars.discourse-cdn.com/v4/letter/r/ac91a4/32.png) [@ronman](https://forum.graphviz.org/u/ronman)
#### Post date: [August 12, 2021, 1:41am UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/5 "2021-08-12T01:41:59Z")

</div>

Is this what you mean? Real-time multiuser editing is supported. As we speak, I’m working on dot import/export.

Not shown is the “Prettify” function, which is graphviz layout. And the video-in-a-node feature is no longer supported.

> **[Ideatree Network Map](https://ideatree.net/ideatree/video_how_to_use/)**
>
> Simultaneous Collaboration Using Network Graphs

---

<div class="post-metadata">

### Author: ![schmoo2k](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/schmoo2k/32/29_2.png) [@schmoo2k](https://forum.graphviz.org/u/schmoo2k)
#### Post date: [August 12, 2021, 6:40am UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/6 "2021-08-12T06:40:11Z")

</div>

Here is my attempt at a minimal html page:

```auto
<!DOCTYPE html>
<html>

<head>
    <title>SVG Zoom</title>
    <script src="https://cdn.jsdelivr.net/npm/d3-selection@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-dispatch@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-drag@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-zoom@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-transition@3"></script>
    <script>
        window.onload = function () {
            var svg = d3.select("svg");
            var g = svg.select("g");
            var [x, y, width, height] = svg.attr("viewBox").split(" ");
            var zoom = d3.zoom();
            svg.call(zoom
                .extent([[0, 0], [width, height]])
                .scaleExtent([0.1, 8])
                .on("zoom", ({ transform }) => g.attr("transform", transform))
            );
            svg.call(zoom.translateTo, width / 2, -height / 2);
        };
    </script>
</head>

<body>
    <svg width="224pt" height="409pt" viewBox="0.00 0.00 224.00 409.01" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 405.01)">
            <title>G</title>
            <polygon fill="white" stroke="transparent" points="-4,4 -4,-405.01 220,-405.01 220,4 -4,4"></polygon>
            <g id="clust1" class="cluster">
                <title>cluster_0</title>
                <polygon fill="lightgrey" stroke="lightgrey" points="8,-64.21 8,-357.01 98,-357.01 98,-64.21 8,-64.21"></polygon>
                <text text-anchor="middle" x="53" y="-340.41" font-family="Times,serif" font-size="14.00">process #1</text>
            </g>
            <g id="clust2" class="cluster">
                <title>cluster_1</title>
                <polygon fill="none" stroke="blue" points="133,-64.21 133,-357.01 208,-357.01 208,-64.21 133,-64.21"></polygon>
                <text text-anchor="middle" x="170.5" y="-340.41" font-family="Times,serif" font-size="14.00">process #2</text>
            </g>
            <!-- a0 -->
            <g id="node1" class="node">
                <title>a0</title>
                <ellipse fill="white" stroke="white" cx="63" cy="-306.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="63" y="-302.01" font-family="Times,serif" font-size="14.00">a0</text>
            </g>
            <!-- a1 -->
            <g id="node2" class="node">
                <title>a1</title>
                <ellipse fill="white" stroke="white" cx="63" cy="-234.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="63" y="-230.01" font-family="Times,serif" font-size="14.00">a1</text>
            </g>
            <!-- a0&#45;&gt;a1 -->
            <g id="edge1" class="edge">
                <title>a0-&gt;a1</title>
                <path fill="none" stroke="black" d="M63,-287.91C63,-280.2 63,-270.93 63,-262.33"></path>
                <polygon fill="black" stroke="black" points="66.5,-262.32 63,-252.32 59.5,-262.32 66.5,-262.32"></polygon>
            </g>
            <!-- a2 -->
            <g id="node3" class="node">
                <title>a2</title>
                <ellipse fill="white" stroke="white" cx="63" cy="-162.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="63" y="-158.01" font-family="Times,serif" font-size="14.00">a2</text>
            </g>
            <!-- a1&#45;&gt;a2 -->
            <g id="edge2" class="edge">
                <title>a1-&gt;a2</title>
                <path fill="none" stroke="black" d="M63,-215.91C63,-208.2 63,-198.93 63,-190.33"></path>
                <polygon fill="black" stroke="black" points="66.5,-190.32 63,-180.32 59.5,-190.32 66.5,-190.32"></polygon>
            </g>
            <!-- b3 -->
            <g id="node8" class="node">
                <title>b3</title>
                <ellipse fill="lightgrey" stroke="black" cx="168" cy="-90.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="168" y="-86.01" font-family="Times,serif" font-size="14.00">b3</text>
            </g>
            <!-- a1&#45;&gt;b3 -->
            <g id="edge9" class="edge">
                <title>a1-&gt;b3</title>
                <path fill="none" stroke="black" d="M74.44,-217.75C92.74,-192.99 128.75,-144.3 150.37,-115.06"></path>
                <polygon fill="black" stroke="black" points="153.45,-116.78 156.58,-106.66 147.82,-112.62 153.45,-116.78"></polygon>
            </g>
            <!-- a3 -->
            <g id="node4" class="node">
                <title>a3</title>
                <ellipse fill="white" stroke="white" cx="63" cy="-90.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="63" y="-86.01" font-family="Times,serif" font-size="14.00">a3</text>
            </g>
            <!-- a2&#45;&gt;a3 -->
            <g id="edge3" class="edge">
                <title>a2-&gt;a3</title>
                <path fill="none" stroke="black" d="M63,-143.91C63,-136.2 63,-126.93 63,-118.33"></path>
                <polygon fill="black" stroke="black" points="66.5,-118.32 63,-108.32 59.5,-118.32 66.5,-118.32"></polygon>
            </g>
            <!-- a3&#45;&gt;a0 -->
            <g id="edge11" class="edge">
                <title>a3-&gt;a0</title>
                <path fill="none" stroke="black" d="M49.25,-106.15C41.04,-116.11 31.38,-129.97 27,-144.21 12.89,-190.09 12.89,-206.33 27,-252.21 30.29,-262.9 36.54,-273.36 42.93,-282.13"></path>
                <polygon fill="black" stroke="black" points="40.35,-284.53 49.25,-290.28 45.88,-280.24 40.35,-284.53"></polygon>
            </g>
            <!-- end -->
            <g id="node10" class="node">
                <title>end</title>
                <polygon fill="none" stroke="black" points="133.22,-36.32 96.78,-36.32 96.78,0.11 133.22,0.11 133.22,-36.32"></polygon>
                <polyline fill="none" stroke="black" points="108.78,-36.32 96.78,-24.32 "></polyline>
                <polyline fill="none" stroke="black" points="96.78,-11.89 108.78,0.11 "></polyline>
                <polyline fill="none" stroke="black" points="121.22,0.11 133.22,-11.89 "></polyline>
                <polyline fill="none" stroke="black" points="133.22,-24.32 121.22,-36.32 "></polyline>
                <text text-anchor="middle" x="115" y="-13.91" font-family="Times,serif" font-size="14.00">end</text>
            </g>
            <!-- a3&#45;&gt;end -->
            <g id="edge12" class="edge">
                <title>a3-&gt;end</title>
                <path fill="none" stroke="black" d="M74.54,-73.66C80.84,-65.17 88.79,-54.45 95.97,-44.76"></path>
                <polygon fill="black" stroke="black" points="98.88,-46.71 102.03,-36.59 93.26,-42.54 98.88,-46.71"></polygon>
            </g>
            <!-- b0 -->
            <g id="node5" class="node">
                <title>b0</title>
                <ellipse fill="lightgrey" stroke="black" cx="168" cy="-306.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="168" y="-302.01" font-family="Times,serif" font-size="14.00">b0</text>
            </g>
            <!-- b1 -->
            <g id="node6" class="node">
                <title>b1</title>
                <ellipse fill="lightgrey" stroke="black" cx="170" cy="-234.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="170" y="-230.01" font-family="Times,serif" font-size="14.00">b1</text>
            </g>
            <!-- b0&#45;&gt;b1 -->
            <g id="edge4" class="edge">
                <title>b0-&gt;b1</title>
                <path fill="none" stroke="black" d="M168.49,-287.91C168.71,-280.2 168.98,-270.93 169.23,-262.33"></path>
                <polygon fill="black" stroke="black" points="172.72,-262.41 169.51,-252.32 165.73,-262.21 172.72,-262.41"></polygon>
            </g>
            <!-- b2 -->
            <g id="node7" class="node">
                <title>b2</title>
                <ellipse fill="lightgrey" stroke="black" cx="173" cy="-162.21" rx="27" ry="18"></ellipse>
                <text text-anchor="middle" x="173" y="-158.01" font-family="Times,serif" font-size="14.00">b2</text>
            </g>
            <!-- b1&#45;&gt;b2 -->
            <g id="edge5" class="edge">
                <title>b1-&gt;b2</title>
                <path fill="none" stroke="black" d="M170.74,-215.91C171.07,-208.2 171.47,-198.93 171.84,-190.33"></path>
                <polygon fill="black" stroke="black" points="175.34,-190.46 172.27,-180.32 168.34,-190.16 175.34,-190.46"></polygon>
            </g>
            <!-- b2&#45;&gt;a3 -->
            <g id="edge10" class="edge">
                <title>b2-&gt;a3</title>
                <path fill="none" stroke="black" d="M153.84,-149.02C136.33,-137.88 110.24,-121.28 90.51,-108.72"></path>
                <polygon fill="black" stroke="black" points="92.26,-105.68 81.94,-103.27 88.5,-111.59 92.26,-105.68"></polygon>
            </g>
            <!-- b2&#45;&gt;b3 -->
            <g id="edge6" class="edge">
                <title>b2-&gt;b3</title>
                <path fill="none" stroke="black" d="M171.76,-143.91C171.21,-136.2 170.55,-126.93 169.94,-118.33"></path>
                <polygon fill="black" stroke="black" points="173.43,-118.04 169.22,-108.32 166.44,-118.54 173.43,-118.04"></polygon>
            </g>
            <!-- b3&#45;&gt;end -->
            <g id="edge13" class="edge">
                <title>b3-&gt;end</title>
                <path fill="none" stroke="black" d="M156.24,-73.66C149.82,-65.17 141.72,-54.45 134.39,-44.76"></path>
                <polygon fill="black" stroke="black" points="137.04,-42.46 128.22,-36.59 131.46,-46.68 137.04,-42.46"></polygon>
            </g>
            <!-- start -->
            <g id="node9" class="node">
                <title>start</title>
                <polygon fill="none" stroke="black" points="115,-401.01 75.76,-383.01 115,-365.01 154.24,-383.01 115,-401.01"></polygon>
                <polyline fill="none" stroke="black" points="86.67,-388.02 86.67,-378.01 "></polyline>
                <polyline fill="none" stroke="black" points="104.09,-370.02 125.91,-370.02 "></polyline>
                <polyline fill="none" stroke="black" points="143.33,-378.01 143.33,-388.02 "></polyline>
                <polyline fill="none" stroke="black" points="125.91,-396.01 104.09,-396.01 "></polyline>
                <text text-anchor="middle" x="115" y="-378.81" font-family="Times,serif" font-size="14.00">start</text>
            </g>
            <!-- start&#45;&gt;a0 -->
            <g id="edge7" class="edge">
                <title>start-&gt;a0</title>
                <path fill="none" stroke="black" d="M105.94,-368.99C98.71,-358.58 88.37,-343.71 79.66,-331.17"></path>
                <polygon fill="black" stroke="black" points="82.52,-329.16 73.94,-322.95 76.77,-333.16 82.52,-329.16"></polygon>
            </g>
            <!-- start&#45;&gt;b0 -->
            <g id="edge8" class="edge">
                <title>start-&gt;b0</title>
                <path fill="none" stroke="black" d="M124.23,-368.99C131.65,-358.51 142.28,-343.52 151.2,-330.93"></path>
                <polygon fill="black" stroke="black" points="154.12,-332.86 157.04,-322.68 148.41,-328.82 154.12,-332.86"></polygon>
            </g>
        </g>
    </svg>
</body>

</html>

```

---

<div class="post-metadata">

### Author: ![schmoo2k](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/schmoo2k/32/29_2.png) [@schmoo2k](https://forum.graphviz.org/u/schmoo2k)
#### Post date: [August 12, 2021, 6:54am UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/7 "2021-08-12T06:54:30Z")

</div>

Of course if you are going down that road, then you could go one step further:

```auto
<!DOCTYPE html>
<html>

<head>
    <title>SVG Zoom</title>
    <script src="https://cdn.jsdelivr.net/npm/d3-selection@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-dispatch@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-drag@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-zoom@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-transition@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/@hpcc-js/wasm/dist/index.min.js"></script>
    <script>
        var hpccWasm = window["@hpcc-js/wasm"];
        hpccWasm.graphviz.dot(`
digraph G {
  subgraph cluster_0 {
    style=filled;
    color=lightgrey;
    node [style=filled,color=white];
    a0 -> a1 -> a2 -> a3;
    label = "process #${1}";
  }
  subgraph cluster_1 {
    node [style=filled];
    b0 -> b1 -> b2 -> b3;
    label = "process #${2}";
    color=blue
  }
  start -> a0;
  start -> b0;
  a1 -> b3;
  b2 -> a3;
  a3 -> a0;
  a3 -> end;
  b3 -> end;
  start [shape=Mdiamond];
  end [shape=Msquare];
}`
        ).then(svg => {
            d3.select("body").html(svg);
            var svg = d3.select("svg");
            var g = svg.select("g");
            var [x, y, width, height] = svg.attr("viewBox").split(" ");
            var zoom = d3.zoom();
            svg.call(zoom
                .extent([[0, 0], [width, height]])
                .scaleExtent([0.1, 8])
                .on("zoom", ({ transform }) => g.attr("transform", transform))
            );
            svg.call(zoom.translateTo, width / 2, -height / 2);
        });
    </script>
</head>

<body>
</body>

</html>

```

---

<div class="post-metadata">

### Author: ![magjac](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/magjac/32/16_2.png) [@magjac](https://forum.graphviz.org/u/magjac)
#### Post date: [August 12, 2021, 7:59am UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/8 "2021-08-12T07:59:43Z")

</div>

> [@scnorth](#):
>
> I wonder if it would be reasonable to enhance the SVG code generator possibly based on d3-graphviz in graphviz to would emit a standalone viewable graph?

I think that would be an overkill for two reasons:

1. d3-graphviz _contains_ Graphviz through @hpcc-js/wasm and _renders_ the SVG. What we need here is just to pan and zoom an _existing_ SVG outputted by Graphviz.
2. d3-graphviz’s main purpose is to do animated transitions between graphs. If you just need pan and zoom, it’s better to use something more lightweight such as d3-zoom as in Gordon’s proposals above.

> [@smattr](#):
>
> Zooming SVGs in a modern browser is surprisingly pleasant these days.

I agree. At least with Firefox you can do all you need with various combinations of Ctrl, Shift and mouse wheel, although I’d prefer to do panning by dragging as d3-zoom (and d3-graphviz) do.

---

<div class="post-metadata">

### Author: ![smattr](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/smattr/32/85_2.png) [@smattr](https://forum.graphviz.org/u/smattr)
#### Post date: [August 16, 2021, 3:03pm UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/9 "2021-08-16T15:03:32Z")

</div>

FYI I learned from [this post](https://www.pypy.org/posts/2021/04/ways-pypy-graphviz.html) that PyPy have [their own Graphviz viewer](https://foss.heptapod.net/pypy/pypy/-/tree/branch/default/dotviewer) for zooming, panning, etc.

---

<div class="post-metadata">

### Author: ![mark](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/mark/32/84_2.png) [@mark](https://forum.graphviz.org/u/mark)
#### Post date: [August 24, 2021, 9:48am UTC](https://forum.graphviz.org/t/people-ask-for-a-convenient-pan-zoom-search-viewer/768/10 "2021-08-24T09:48:34Z")

</div>

pprof also has a scrollable Graphviz UI: [https://twitter.com/rakyll/status/899799394907594752](https://twitter.com/rakyll/status/899799394907594752) [https://github.com/google/pprof](https://github.com/google/pprof)
