diff --git a/ivette/package.json b/ivette/package.json
index 3dacbd3f46bbd1ba6eefa95b300aa254cda32991..f6cd03b5b37c091e5ec7a2e750f17f4e73a6fa8e 100644
--- a/ivette/package.json
+++ b/ivette/package.json
@@ -62,6 +62,7 @@
     "cytoscape-cxtmenu": "^3.1.2",
     "cytoscape-dagre": "^2.2.2",
     "cytoscape-klay": "^3.1.3",
+    "cytoscape-panzoom": "^2.5.3",
     "cytoscape-popper": "^1.0.7",
     "immutable": "^4.0.0-rc.12",
     "lodash": "^4.17.15",
diff --git a/ivette/src/frama-c/dive/Dive.tsx b/ivette/src/frama-c/dive/Dive.tsx
index c0480f5b4b54d3bf747390caaceb846ed712fb3b..e77030f430cdbb1f67650ec47eced1ac3b885f8f 100644
--- a/ivette/src/frama-c/dive/Dive.tsx
+++ b/ivette/src/frama-c/dive/Dive.tsx
@@ -10,6 +10,7 @@ import * as API from 'api/plugins/dive';
 import Cytoscape from 'cytoscape';
 import CytoscapeComponent from 'react-cytoscapejs';
 import './cytoscape_libs';
+import 'cytoscape-panzoom/cytoscape.js-panzoom.css';
 
 import tippy, * as Tippy from 'tippy.js';
 import 'tippy.js/dist/tippy.css';
@@ -34,6 +35,7 @@ interface Cxtcommand {
 
 interface CytoscapeExtended extends Cytoscape.Core {
   cxtmenu(options: any): void;
+  panzoom(options: any): void;
 }
 
 function callstackToString(callstack: API.callstack): string {
@@ -52,6 +54,19 @@ function buildCxtMenu(
   });
 }
 
+/* double click events for Cytoscape */
+
+function enableDoubleClickEvents(cy: Cytoscape.Core, delay = 350) {
+  let last: Cytoscape.EventObject | undefined;
+  cy.on('click', (e) => {
+    if (last && last.target === e.target &&
+      e.timeStamp - last.timeStamp < delay) {
+      e.target.trigger('double-click', e);
+    }
+    last = e;
+  });
+}
+
 /* The Dive class handles the selection of nodes according to user actions.
    To prevent cytoscape to automatically select (and unselect) nodes wrongly,
    we make some nodes unselectable. We then use the functions below to make
@@ -83,8 +98,17 @@ class Dive {
     this.cy = cy || Cytoscape();
     this.headless = this.cy.container() === null;
     this.cy.elements().remove();
-    this.cy.off('click'); // Remove previous listeners
+
+    // Remove previous listeners
+    this.cy.off('click');
+    this.cy.off('double-click');
+
+    // Add new listeners
+    enableDoubleClickEvents(this.cy);
     this.cy.on('click', 'node', (event) => this.clickNode(event.target));
+    this.cy.on('double-click', '$node > node', // compound nodes
+      (event) => this.doubleClickNode(event.target));
+    (this.cy as CytoscapeExtended).panzoom({});
 
     this.layout = 'cose-bilkent';
 
@@ -436,6 +460,10 @@ class Dive {
     node.unselectify();
   }
 
+  doubleClickNode(node: Cytoscape.NodeSingular) {
+    this.cy.animate({ fit: { eles: node, padding: 10 } });
+  }
+
   selectLocation(location: States.Location | undefined, doExplore: boolean) {
     if (!location) {
       // Reset whole graph if no location is selected.
diff --git a/ivette/src/frama-c/dive/cytoscape_libs.js b/ivette/src/frama-c/dive/cytoscape_libs.js
index 0bd1aced540e77450152e649c1475d3326898915..35ecb1b1ffdebe5bed3e5964a17aad4e492479d3 100644
--- a/ivette/src/frama-c/dive/cytoscape_libs.js
+++ b/ivette/src/frama-c/dive/cytoscape_libs.js
@@ -2,18 +2,23 @@
 This prevents Hot Module Reloading for modules where Cytescope.use is used.
 Grouping all Cytoscape plugins registrations here solves the problem. */
 
-import Cytoscape from 'cytoscape' ;
+import Cytoscape from 'cytoscape';
 
-import CytoscapeMenu from 'cytoscape-cxtmenu';
-import CytoscapePopper from 'cytoscape-popper';
-import CytoscapeLayoutDagre from 'cytoscape-dagre';
-import CytoscapeLayoutCola from 'cytoscape-cola';
-import CytoscapeLayoutCoseBilkent from 'cytoscape-cose-bilkent';
-import CytoscapeLayoutKlay from 'cytoscape-klay';
+import CxtMenu from 'cytoscape-cxtmenu';
+import Popper from 'cytoscape-popper';
+import Panzoom from 'cytoscape-panzoom';
 
-Cytoscape.use(CytoscapePopper);
-Cytoscape.use(CytoscapeMenu);
-Cytoscape.use(CytoscapeLayoutDagre);
-Cytoscape.use(CytoscapeLayoutCola);
-Cytoscape.use(CytoscapeLayoutCoseBilkent);
-Cytoscape.use(CytoscapeLayoutKlay);
+// Layouts
+import Dagre from 'cytoscape-dagre';
+import Cola from 'cytoscape-cola';
+import CoseBilkent from 'cytoscape-cose-bilkent';
+import Klay from 'cytoscape-klay';
+
+Cytoscape.use(Popper);
+Cytoscape.use(CxtMenu);
+Panzoom(Cytoscape); // register extension
+
+Cytoscape.use(Dagre);
+Cytoscape.use(Cola);
+Cytoscape.use(CoseBilkent);
+Cytoscape.use(Klay);
diff --git a/ivette/yarn.lock b/ivette/yarn.lock
index 1402c44ae3bc9d7200f381f5fd57207ac495b66b..e708d53b0660333948b3107c0e6c76eb09631b01 100644
--- a/ivette/yarn.lock
+++ b/ivette/yarn.lock
@@ -3008,6 +3008,13 @@ cytoscape-klay@^3.1.3:
   dependencies:
     klayjs "^0.4.1"
 
+cytoscape-panzoom@^2.5.3:
+  version "2.5.3"
+  resolved "https://registry.yarnpkg.com/cytoscape-panzoom/-/cytoscape-panzoom-2.5.3.tgz#edf041b5aa8be1cbe3c001f16a8b2193b46127a7"
+  integrity sha512-//qLOqbbFUCGddarNKHDZArItOJHgnkQ1TvxI9nV2/8aOOl/5wuEOHmra3fL/aWSjB4AYpYTG4LX7w96uWfRTQ==
+  dependencies:
+    jquery "^1.4 || ^2.0 || ^3.0"
+
 cytoscape-popper@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/cytoscape-popper/-/cytoscape-popper-1.0.7.tgz#8154ff507d0cc1a17952f00643e71fc1f8ea9fae"
@@ -5396,6 +5403,11 @@ jest-worker@^25.1.0:
     merge-stream "^2.0.0"
     supports-color "^7.0.0"
 
+"jquery@^1.4 || ^2.0 || ^3.0":
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
+  integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
+
 js-base64@^2.1.9:
   version "2.5.2"
   resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209"