JSFiddle - React, Tailwind, and code Playground
HTML
<div id="3d-graph"></div>
JavaScript
import SpriteText from "https://esm.sh/three-spritetext";
import * as THREE from 'https://esm.sh/three';
import * as _ from 'https://cdn.jsdelivr.net/npm/3d-force-graph';
function main(data){ // introduced for readability, so the data is defined at the end
const elem = document.getElementById('3d-graph');
const imgs = ['Hashtag3.png'];
const Graph = new ForceGraph3D(elem)
.graphData(data)
.nodeLabel(node => `${node.user}: ${node.description}`)
.nodeThreeObjectExtend(true)
.onNodeClick(node => {
// Aim at node from outside it
const distance = 40;
const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z);
const newPos = node.x || node.y || node.z
? {x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio}
: {x: 0, y: 0, z: distance}; // special case if node is in (0,0,0)
Graph.cameraPosition(
newPos, // new position
node, // lookAt ({ x, y, z })
3000 // ms transition duration
);
})
.nodeAutoColorBy('group')
.nodeLabel('text') //scholar or #keyword or discipline
.nodeLabel(node => `${node.text = `Click to see `}${node.label}${node.text = `'s profile`}`)
.nodeThreeObject(node => {
const textSprite = new SpriteText(node.label ?? node.id);
textSprite.material.depthWrite = false; // make sprite background transparent
textSprite.color = node.color;
textSprite.center.y = -1.2; // shift above node
if(node.type === "keywords"){
const imgTexture = new THREE.TextureLoader().load(`https://raw.githubusercontent.com/greg-gan/JTC_Oak_visualization_trial/refs/heads/main/imgs/hashtag.png`);
imgTexture.colorSpace = THREE.SRGBColorSpace;
const material = new THREE.SpriteMaterial({map: imgTexture});
const sprite = new THREE.Sprite(material);
...