diff --git a/datasets/aud1.json b/datasets/aud1.json
index 0afcb96c28774741acc5e14368430e4bec068b78..0909f9a5cef98a2749b3ecba67480e78c07b44c2 100644
--- a/datasets/aud1.json
+++ b/datasets/aud1.json
@@ -24,7 +24,8 @@
     },
     {
       "id":  "id6",
-      "name": "Eulertouren"
+      "name": "Eulertouren",
+      "image": "eulertouren.png"
 
     },
     {
diff --git a/datasets/images/alpha.png b/datasets/images/alpha.png
new file mode 100644
index 0000000000000000000000000000000000000000..94e03360f41349d7c37fe9ce441ea964f7494c26
Binary files /dev/null and b/datasets/images/alpha.png differ
diff --git a/datasets/images/eulertouren.png b/datasets/images/eulertouren.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6e5b7b82e0597b8db45e7d3be5af5a39f9a52cc
Binary files /dev/null and b/datasets/images/eulertouren.png differ
diff --git a/graph.js b/graph.js
index b07e7561a036464047b651f15f966294838f2d73..48d21f4bd8ef76c43b785c15ca13409cb4134075 100644
--- a/graph.js
+++ b/graph.js
@@ -13,19 +13,23 @@ loadGraph();
 async function loadGraph() {
     const dataUrl = plugin_path + 'datasets/aud1.json'
     const gData = await fetch(dataUrl).then(res => res.json())
-    Graph = ForceGraph3D()
+    Graph = ForceGraph3D({extraRenderers: [new THREE.CSS2DRenderer(), new THREE.CSS3DRenderer()]})
     (document.getElementById('3d-graph'))
         .graphData(gData)
         .nodeLabel('id')
         .nodeAutoColorBy('group')
         .nodeColor(node => highlightNodes.has(node) ? node === hoverNode ? 'rgb(255,0,0,1)' : 'rgba(255,160,0,0.8)' : 'rgba(0,255,255,0.6)')
-        .linkWidth(link => highlightLinks.has(link) ? 4 : 1)
+        .linkWidth(link => highlightLinks.has(link) ? 2 : 0.8)
         .onNodeClick(handleNodeClick)
         .onNodeHover(nodeHover)
         .onLinkHover(linkHover)
         .linkColor(linkColor)
         .linkOpacity(0.8)
-        .onEngineTick(loadComponents).width(getWidth());
+        .nodeThreeObjectExtend(false)
+        .nodeThreeObject(drawNode)
+        .onEngineTick(loadComponents)
+        .width(getWidth());
+    //Graph.renderer().sortObjects = true;
 }
 
 
@@ -63,6 +67,38 @@ function getCanvasDivNode() {
     return domNode.firstChild.firstChild.firstChild;
 }
 
+function drawNode(node) {
+    // Draw node as label + image
+    const nodeDiv = document.createElement('div');
+    group = new THREE.Group();
+
+    const labelDiv = document.createElement('div')
+    labelDiv.textContent = node.name;
+    labelDiv.style.color = node.color;
+    labelDiv.className = 'node-label';
+    nodeDiv.appendChild(labelDiv);
+
+    const cssobj = new THREE.CSS3DSprite(nodeDiv);
+    cssobj.scale.set(0.25, 0.25, 0.25);
+
+    if ('image' in node) {
+        cssobj.position.set(0, -7, 0);
+
+        const textureLoader = new THREE.TextureLoader();
+        const imageTexture = textureLoader.load(plugin_path + 'datasets/images/' + node.image);
+        const imageAlpha = textureLoader.load(plugin_path + 'datasets/images/alpha.png');
+        const material = new THREE.SpriteMaterial({map: imageTexture, alphaMap: imageAlpha, transparent: true,
+            alphaTest: 0.2, depthWrite:false, depthTest: false});
+        const sprite = new THREE.Sprite(material);
+        sprite.renderOrder = 999; // This may not be optimal. But it allows us to render the sprite on top of everything else.
+        sprite.scale.set(20, 20);
+        group.add(sprite)
+    }
+
+    group.add(cssobj)
+    return group;
+}
+
 
 function createInfoOverlay() {
     const sceneNode = getCanvasDivNode();
diff --git a/kg-style.css b/kg-style.css
index 3bf3dbf6a935a6b80beeb53a1248c4a63b9250b8..070e16692ab3c82d0c13618529ca3e4c6162e26a 100644
--- a/kg-style.css
+++ b/kg-style.css
@@ -2,6 +2,29 @@
     display: flex;
 }
 
+.node-image {
+    width: 20px;
+    height: 20px;
+    position: relative;
+    overflow: hidden;
+    border-radius: 50%;
+    display: inline;
+    margin: 0 auto;
+}
+
+
+.node-label {
+    font-size: 14px;
+    color: white;
+    font-weight: bold;
+    /*text-align: center;*/
+    padding: 1px 4px;
+    border-radius: 4px;
+    background-color: rgba(0,0,0,0.7);
+    user-select: none;
+    z-index: 1;
+}
+
 .close-button {
     pointer-events: all;
     z-index: 99;
@@ -21,6 +44,7 @@
     height: 100%;
     background-color: whitesmoke;
     padding: 10px;
+    z-index: 20;
 }
 
 .detail-view-headline {
diff --git a/knowledge-space.php b/knowledge-space.php
index 9cd5f4747c9079bd7c5ee3b3ce6519760508b8ca..b03271b7dd01d9c0f8afc1175a4a788e4eeba63f 100644
--- a/knowledge-space.php
+++ b/knowledge-space.php
@@ -11,6 +11,8 @@ function ks_add_graph(): string
 {
     $graph = '<script src="//unpkg.com/3d-force-graph"></script>';
     $three = '<script src="//unpkg.com/three"></script>';
+    $renderer = '<script src="//unpkg.com/three/examples/js/renderers/CSS2DRenderer.js"></script>';
+    $renderer2 = '<script src="//unpkg.com/three/examples/js/renderers/CSS3DRenderer.js"></script>';
     $div = '<div id="3d-graph"></div>';
     $plugin_dir = plugin_dir_url(__FILE__);
     //$dataset = $plugin_dir.'datasets/miserables.json';
@@ -20,7 +22,7 @@ function ks_add_graph(): string
     $script_path = $plugin_dir.'graph.js';
     $script = "<script src='$script_path'></script>";
 
-    return $graph . $three . $div . $variables . $script;
+    return $three . $renderer .$renderer2 . $graph . $div . $variables . $script;
 }
 
 function kg_load_css() {