BokehJS Support for 3D Scatter, Line and Bar plots

A custom BokehJS extension that visualizes 3D scatter points, lines and bars.

Free and open source code is here:

Simply download the folder, install bokeh numpy and run the EXAMPLES_3d.py.

Simple example:

import numpy as np
from bokeh.plotting import show, output_file
from scatter3d_py import Scatter3D

np.random.seed(42)
n_points = 200
x = np.random.randn(n_points) * 2
y = np.random.randn(n_points) * 2
z = np.random.randn(n_points) * 2

# Color based on distance from origin
colors = np.sqrt(x**2 + y**2 + z**2)

scatter1 = Scatter3D(
    x=x.tolist(),
    y=y.tolist(),
    z=z.tolist(),
    colors=colors.tolist(),
    palette='Viridis256',
    default_size=6.0,
    autorotate=False,
    rotation_speed=1.0,
    width=800,
    height=800,
    background_color='#0a0a0a',
    colorbar_title='Distance',
    show_colorbar=True,
    enable_hover=True,
)

output_file("scatter3d_basic.html", title="3D Scatter - Basic")
show(scatter1)

Peek 2026-02-15 03-31

import numpy as np
from bokeh.plotting import show, output_file
from scatter3d_py import Scatter3D

n_per_cluster = 80
clusters = []
cluster_centers = [(0, 0, 0), (5, 5, 2), (-3, 4, -2)]
cluster_colors = [0, 1, 2]

x_all, y_all, z_all, colors_all = [], [], [], []
labels_all = []

for i, (cx, cy, cz) in enumerate(cluster_centers):
    x_cluster = np.random.randn(n_per_cluster) * 0.8 + cx
    y_cluster = np.random.randn(n_per_cluster) * 0.8 + cy
    z_cluster = np.random.randn(n_per_cluster) * 0.8 + cz
    
    x_all.extend(x_cluster)
    y_all.extend(y_cluster)
    z_all.extend(z_cluster)
    colors_all.extend([cluster_colors[i]] * n_per_cluster)
    labels_all.extend([f"Cluster {i+1}"] * n_per_cluster)

scatter3 = Scatter3D(
    x=x_all,
    y=y_all,
    z=z_all,
    colors=colors_all,
    labels=labels_all,
    palette='Set1',
    default_size=5.0,
    show_outline=True,
    outline_color='#ffffff',
    outline_width=1.0,
    autorotate=True,
    width=800,
    height=800,
    background_color='#0a0a0a',
    colorbar_title='Cluster',
    vmin=0,
    vmax=2,
)

output_file("scatter3d_clusters.html", title="3D Scatter - Clustered Data")
show(scatter3)

Peek 2026-02-15 03-33

import numpy as np
from bokeh.plotting import show, output_file
from scatter3d_py import Scatter3D

t = np.linspace(0, 4*np.pi, 150)
x = t * np.cos(t)
y = t * np.sin(t)
z = t

# Size increases with time
sizes = t / t.max() * 10

scatter2 = Scatter3D(
    x=x.tolist(),
    y=y.tolist(),
    z=z.tolist(),
    colors=z.tolist(),
    sizes=sizes.tolist(),
    palette='Plasma256',
    min_size=2.0,
    max_size=20.0,
    autorotate=False,
    width=800,
    height=800,
    background_color='#0a0a0a',
    colorbar_title='Height',
    azimuth=60,
    elevation=25,
)

output_file("scatter3d_spiral.html", title="3D Scatter - Spiral with Variable Sizes")
show(scatter2)

import numpy as np
from bokeh.plotting import show, output_file
from bar3d_py import Bar3D


n_x, n_y = 5, 5
x_grid = []
y_grid = []
values_grid = []

for i in range(n_x):
    for j in range(n_y):
        x_grid.append(i)
        y_grid.append(j)
        # Height based on distance from center
        dx = i - n_x/2
        dy = j - n_y/2
        values_grid.append(5 - np.sqrt(dx**2 + dy**2))

bar1 = Bar3D(
    x=x_grid,
    y=y_grid,
    values=values_grid,
    bar_width=0.6,
    bar_depth=0.6,
    palette='Turbo256',
    autorotate=True,
    width=800,
    height=800,
    background_color='#0a0a0a',
    colorbar_title='Height',
    elevation=-35,
    azimuth=50,
)

output_file("bar3d_grid.html", title="3D Bar Chart - Grid Pattern")
show(bar1)

import numpy as np
from bokeh.plotting import show, output_file
from line3d_py import Line3D

n_steps = 500
x = np.cumsum(np.random.randn(n_steps) * 0.1)
y = np.cumsum(np.random.randn(n_steps) * 0.1)
z = np.cumsum(np.random.randn(n_steps) * 0.1)

# Color by time/step
colors = np.arange(n_steps)

line3 = Line3D(
    x=x.tolist(),
    y=y.tolist(),
    z=z.tolist(),
    colors=colors.tolist(),
    palette='Plasma256',
    line_width=2.5,
    show_markers=True,
    marker_size=2.0,
    autorotate=True,
    width=800,
    height=800,
    background_color='#0a0a0a',
    colorbar_title='Time Step',
)

output_file("line3d_random_walk.html", title="3D Line - Random Walk")
show(line3)

Peek 2026-02-15 03-34

3 Likes