A Python-first visualization and editing tool for atomic structures with real-time collaboration.
You can install zndraw into your Python environment via pip:
pip install zndrawor set it up as uv tool to run anywhere:
uvx zndraw <file>Visualize your trajectories with a single command:
zndraw <file>Note
ZnDraw's webapp-based approach allows you to use port forwarding to work with trajectories on remote systems.
ZnDraw supports multiple clients connecting to the same visualization. Each visualization session is identified by a room name visible in the URL.
from zndraw import ZnDraw
vis = ZnDraw(url="http://localhost:1234", room="my-room")For protected deployments, provide credentials:
vis = ZnDraw(
url="http://localhost:1234",
room="my-room",
user="username",
password="password"
)If no credentials are provided, the server assigns a guest user.
The vis object behaves like a Python list of ase.Atoms objects. Changes are synchronized in real-time across all connected clients.
from ase.collections import s22
# Add structures
vis.extend(list(s22))
# Access current frame
atoms = vis[vis.step]
# Iterate over frames
for atoms in vis:
print(atoms)
# Slice operations
subset = vis[10:20]Control various aspects of the visualization:
vis.selection # Currently selected atoms
vis.step # Current frame index
vis.figures # Plotly figures
vis.bookmarks # Saved frame annotations
vis.geometries # 3D geometry overlays (dict-like)
vis.settings # Room configurationAdd 3D geometry overlays to your visualization:
from zndraw.geometries import Box, Sphere, Arrow, Camera, Curve
vis.geometries["box"] = Box(position=(0, 1, 2))Available geometry types: Sphere, Arrow, Bond, Curve, Cell, Floor, Box, Plane, Shape, Camera.
ZnDraw integrates with Plotly for interactive data visualization. It automatically detects available properties and provides selection menus.
Create custom tools accessible via the ZnDraw UI:
from zndraw import Extension
from molify import smiles2atoms
class AddMolecule(Extension):
smiles: str
def run(self, vis, **kwargs) -> None:
vis.append(smiles2atoms(self.smiles))
vis.step = len(vis) - 1
vis.register(AddMolecule, public=True)
vis.wait()Extensions can be registered as public (available to all clients) or private (only to the registering client).
A hosted version is available at https://zndraw.icp.uni-stuttgart.de
zndraw <file> --url https://zndraw.icp.uni-stuttgart.deZnDraw can be deployed using Docker for both development and production environments.
See the docker/ directory for complete deployment configurations:
- Production deployment - Scalable multi-replica setup with nginx
- Development setup - Hot reload for local development
If you use ZnDraw in your research, please cite:
@article{elijosius2025zero,
title = {Zero-shot molecular generation via similarity kernels},
author = {Elijo{\v s}ius, Rokas and Zills, Fabian and Batatia, Ilyes and Norwood, Sam Walton and Kov{\'a}cs, D{\'a}vid P{\'e}ter and Holm, Christian and Cs{\'a}nyi, G{\'a}bor},
journal = {Nature Communications},
volume = {16},
pages = {5479},
year = {2025},
doi = {10.1038/s41467-025-60963-3},
url = {https://doi.org/10.1038/s41467-025-60963-3},
}The creation of ZnDraw was supported by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) in the framework of the priority program SPP 2363, "Utilization and Development of Machine Learning for Molecular Applications - Molecular Machine Learning" Project No. 497249646. Further funding through the DFG under Germany's Excellence Strategy - EXC 2075 - 390740016 and the Stuttgart Center for Simulation Science (SimTech) was provided.







