Lightweight Three.js viewer controlled from Python via WebSocket
Project description
threejs-viewer
Lightweight Three.js viewer controlled from Python via WebSocket.
A Python client runs a WebSocket server that a browser-based Three.js viewer connects to. Designed for robotics visualization, scientific computing, and interactive 3D exploration.
Features
- Simple API: Add primitives, load models, update transforms
- PBR materials: Roughness, metalness, transparency and environment reflections on all objects
- GLB/PBR support: Load GLB models with PBR materials, embedded skeletal/morph animations
- Animation support: Pre-compute animations, scrub timeline, adjust playback speed
- Binary channels: Efficient transfer of large animations (100k+ objects × frames)
- Bead meshes: Extruded toolpath visualization with per-layer colors and draw_range animation
- Auto-reconnect: Browser reconnects automatically, animations persist
- Z-up coordinates: Robotics convention (matches ROS, URDF)
- No build step: Self-contained HTML viewer, just open in browser
Installation
pip install threejs-viewer
Quick Start
from threejs_viewer import viewer
# Start server and wait for browser to connect
v = viewer()
# Add objects
v.add_sphere("ball", radius=0.3, color=0xFF0000, position=[0, 0, 0.5])
v.add_box("ground", width=5, height=5, depth=0.1, color=0x444444)
# Keep running
input("Press Enter to exit")
The viewer opens automatically in your default browser. To open it manually:
threejs-viewer open
# Or: threejs-viewer path (prints path to viewer.html)
Usage
Objects
from threejs_viewer import Toolpath, viewer
# Primitives with PBR materials
v.add_box("box1", width=1, height=2, depth=0.5, color=0x4A90D9, roughness=0.5, metalness=0.1)
v.add_sphere("ball", radius=0.5, position=[2, 0, 0], roughness=0.3, metalness=0.7)
v.add_cylinder("cyl1", radius_top=0.3, radius_bottom=0.5, height=1)
# 3D models (binary transfer)
v.add_model_binary("robot", "robot.stl", format="stl")
# Polylines with colormaps
v.add_polyline("path", points, colors=z_values, colormap="viridis", line_width=3)
# Bead mesh (extruded toolpath)
tp = Toolpath.from_points(points, bead_width=0.3, bead_height=0.08)
tp.colorize(per_point_rgb)
v.add_mesh("bead", **tp.to_mesh())
# Transparency
v.set_opacity("box1", 0.5)
v.set_color("ball", 0xFF0000, opacity=0.3)
Transforms
# Single object
v.set_matrix("box1", matrix_4x4.flatten().tolist())
# Batch update (efficient for 60fps)
v.batch_update({
"link1": {"position": [1, 2, 0.5]},
"link2": {"position": [3, 0, 1], "rotation": [0, 0, 1.57]},
})
Animations
from threejs_viewer import Animation
animation = Animation(loop=True)
for t in times:
animation.add_frame(
time=t,
transforms=compute_transforms(t),
colors={"robot": 0xFF0000 if collision else 0x00FF00},
clip_times={"glb_model": t}, # drive embedded GLTF animations
)
animation.add_marker(3.5, "Collision detected")
client.load_animation(animation)
Viewer controls: Space (play/pause), Arrow keys (step frames), 1-5 (speed), L (loop)
Binary Channels (Large Animations)
For animations with many objects or frames, use binary channels instead of Frame dicts for much faster serialization and transfer:
import numpy as np
from threejs_viewer import Animation
animation = Animation(loop=True)
animation.set_frame_times(np.arange(n_frames) / 60.0)
# Transforms: (n_frames, n_objects, 16) float32
animation.set_transform_data(object_ids, transform_array)
# Draw ranges: (n_frames, n_objects) float32
animation.set_draw_range_data(object_ids, draw_range_array)
# Clip times for embedded GLTF animations: (n_frames, n_objects) float32
animation.set_clip_time_data(object_ids, clip_time_array)
# Colors with indexed colormap: (n_frames, n_objects) uint8
animation.add_channel("colors", object_ids, color_indices, dtype="uint8",
metadata={"colormap": [0x44AA44, 0xFF3333]})
# Visibility: (n_frames, n_objects) uint8 (0=hidden, 1=visible)
animation.add_channel("visibility", object_ids, vis_data, dtype="uint8")
client.load_animation(animation)
Binary channels and Frame-based JSON can be mixed. A binary channel supersedes the same-named Frame field.
GLB Models with Embedded Animations
# Load a GLB with embedded animations (skeletal, morph targets)
client.add_model_binary("fox", "fox.glb", format="glb")
# Seek embedded animation to a specific time (seconds)
client.set_clip_time("fox", 1.5)
Documentation
CLI
threejs-viewer path # Print path to viewer.html
threejs-viewer open # Open in default browser
threejs-viewer code # Open in VS Code (use "Show Preview" for docked view)
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file threejs_viewer-0.0.13.tar.gz.
File metadata
- Download URL: threejs_viewer-0.0.13.tar.gz
- Upload date:
- Size: 45.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67c31b262b676985b68f123903db27bc8c89fcc4a56681884cc5230442eae6ed
|
|
| MD5 |
6036204af8b999bd65a78fac69f6aab1
|
|
| BLAKE2b-256 |
179961f539668c64b3323d27b8d9eb55334ba05e851f8861453d82eae5163a4a
|
Provenance
The following attestation bundles were made for threejs_viewer-0.0.13.tar.gz:
Publisher:
build-and-release.yml on CEAD-group/threejs-viewer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threejs_viewer-0.0.13.tar.gz -
Subject digest:
67c31b262b676985b68f123903db27bc8c89fcc4a56681884cc5230442eae6ed - Sigstore transparency entry: 1123239741
- Sigstore integration time:
-
Permalink:
CEAD-group/threejs-viewer@6890a63e49c5b06c467d01ee6f9348dc27d81ccd -
Branch / Tag:
refs/tags/v0.0.13 - Owner: https://github.com/CEAD-group
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build-and-release.yml@6890a63e49c5b06c467d01ee6f9348dc27d81ccd -
Trigger Event:
push
-
Statement type:
File details
Details for the file threejs_viewer-0.0.13-py3-none-any.whl.
File metadata
- Download URL: threejs_viewer-0.0.13-py3-none-any.whl
- Upload date:
- Size: 44.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
655ab5ddad0d5695526348da9bc38e6cead980f09fcf5dddf536742dc0190f41
|
|
| MD5 |
007bfd1d516d0b46b96cb9e1c06b30b5
|
|
| BLAKE2b-256 |
c63f0cafd88cee37eb491a3a8dba215d853fda0c11b72863fc74975817462596
|
Provenance
The following attestation bundles were made for threejs_viewer-0.0.13-py3-none-any.whl:
Publisher:
build-and-release.yml on CEAD-group/threejs-viewer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threejs_viewer-0.0.13-py3-none-any.whl -
Subject digest:
655ab5ddad0d5695526348da9bc38e6cead980f09fcf5dddf536742dc0190f41 - Sigstore transparency entry: 1123239748
- Sigstore integration time:
-
Permalink:
CEAD-group/threejs-viewer@6890a63e49c5b06c467d01ee6f9348dc27d81ccd -
Branch / Tag:
refs/tags/v0.0.13 - Owner: https://github.com/CEAD-group
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build-and-release.yml@6890a63e49c5b06c467d01ee6f9348dc27d81ccd -
Trigger Event:
push
-
Statement type: