Skip to content

SurfaceViewer

Cube-backed isosurface viewer for orbital, density, and two-cube delta-density data.

SurfaceViewer renders on top of the same browser runtime as MolecularViewer, so the standard molecule controls, frame slider, camera presets, PNG export, and GIF export remain available for surface pages. GIF export uses an opaque viewer background by default to avoid transparent frame trails; PNG export can still be transparent.

Install the optional surface dependencies before using it:

pip install "aseview[surface]"

See the Surface Viewer example for complete single-cube, density, diff, and multi-isovalue snippets.

Constructor

SurfaceViewer(source, **kwargs)

Parameters

Parameter Type Description Default
source str, Path, cube.Cube, cube.GridField, payload Cube-like input or a prebuilt compact surface payload Required
surface_kind str "orbital" or "density" for single-cube inputs "orbital"
dataset str, int, None Dataset selector for multi-dataset cube inputs None
molecule ASE Atoms, viewer dict, path, list, None Optional molecule context shown with the surface None
theme str, None Visual theme. None uses the global default. None

Use SurfaceViewer.from_cube_diff(left, right, ...) for two-cube delta-density surfaces. Use SurfaceViewer(source, surface_kind="delta_density") when source is already a signed CHGDIFF/charge-density-difference field.

Surface Options

Parameter Type Description Default
isovalue float, list[float], str, "auto" Surface level, multiple shell levels, or a range spec "auto"
surface_color str Color for unsigned density shells "#3b82f6"
positive_color str Color for positive orbital or delta-density shells "#ef4444"
negative_color str Color for negative orbital or delta-density shells "#2563eb"
opacity float Maximum shell opacity 0.65
opacity_ramp list[float], None Per-isovalue opacity values. Length must match isovalue=[...]. None
surface_style str "solid", "mesh", "contour", or "dot" "solid"
quality str "low", "medium", or "high" extraction budget "medium"
max_grid_points int, None Explicit grid downsampling budget None
max_triangles int, None Explicit triangle budget None
smoothing float, None Optional scalar-field smoothing before extraction None
normalize_orbital bool Normalize orbital data before isosurface extraction True

Display Options

Parameter Type Description Default
show_molecule bool Show the optional molecule context True
show_cell bool Show the molecule cell when available True
show_bonds bool Show molecule bonds when available True

Camera options such as viewMode, viewPreset, viewDirection, viewEuler, viewUp, viewFit, and rotationMode follow the same conventions as the other Python viewers.

isovalue range strings are accepted anywhere a Python or CLI surface isovalue is parsed:

Syntax Meaning
"0.02,0.04,0.08" Explicit levels
"0.02:0.08:4" NumPy-style evenly spaced count, equivalent to 4 points from start to stop
"linspace(0.02,0.08,4)" Explicit linspace form with both endpoints included

surface_style="contour" uses a denser default extraction grid than solid or mesh surfaces and renders dense horizontal slice lines instead of mesh edges.

Solid Surface Transparency

surface_style="solid" keeps transparent orbital and density shells readable with the molecule layer by rendering each transparent shell as separate back-face and front-face passes. The front pass uses opacity; the back pass is attenuated so overlapping lobes remain visible without creating the hard clipping artifacts that depth pre-passes can introduce. Opaque shells still use a single mesh pass.

This is a practical browser-rendering compromise for Three.js r128. It improves the usual orbital and charge-density cases, but it is not full order-independent transparency for every possible self-overlapping mesh.

Single Orbital Cube

from aseview import SurfaceViewer

viewer = SurfaceViewer(
    "HOMO.cube",
    surface_kind="orbital",
    isovalue=0.04,
    positive_color="#ef4444",
    negative_color="#2563eb",
)
viewer.show()

Density Cube

from aseview import SurfaceViewer

viewer = SurfaceViewer(
    "density.cube",
    surface_kind="density",
    isovalue=0.002,
    surface_color="#22c55e",
    opacity=0.55,
)
viewer.save_html("density-surface.html")

Two-Cube Difference

from aseview import SurfaceViewer

viewer = SurfaceViewer.from_cube_diff(
    "state-a-density.cube",
    "state-b-density.cube",
    diff_sign="1-2",
    isovalue=0.0015,
    positive_color="#f97316",
    negative_color="#0ea5e9",
)
viewer.show()

Precomputed Difference Field

from aseview import SurfaceViewer

viewer = SurfaceViewer(
    "CHGDIFF.vasp",
    surface_kind="delta_density",
    isovalue=0.02,
    positive_color="#f97316",
    negative_color="#2563eb",
)
viewer.show()

Surface Sequences

Use SurfaceViewer.sequence() when the surface geometry changes frame by frame. The Python API takes an explicit list of frame dictionaries; each frame can contain one surface or a named surfaces mapping.

from aseview import SurfaceViewer

frames = [
    {
        "label": "0 fs",
        "molecule": atoms_0,
        "surfaces": {
            "HOMO": "frame_000/HOMO.cube",
            "LUMO": "frame_000/LUMO.cube",
        },
    },
    {
        "label": "1 fs",
        "molecule": atoms_1,
        "surfaces": {
            "HOMO": "frame_001/HOMO.cube",
            "LUMO": "frame_001/LUMO.cube",
        },
    },
]

viewer = SurfaceViewer.sequence(frames, surface_kind="orbital", isovalue=0.03)
viewer.save_html("homo-lumo-trajectory.html")

For a single frame with multiple related density fields, put each field under a surface label. Two-path entries are treated as delta-density surfaces unless a different surface_kind is set explicitly:

viewer = SurfaceViewer.sequence(
    [
        {
            "label": "adsorbate comparison",
            "surfaces": {
                "AB": {"path": "AB_CHGCAR", "surface_kind": "density"},
                "A": {"path": "A_CHGCAR", "surface_kind": "density"},
                "B": {"path": "B_CHGCAR", "surface_kind": "density"},
                "AB - A": {
                    "paths": ["AB_CHGCAR", "A_CHGCAR"],
                    "isovalue": 0.01,
                },
            },
        }
    ],
    surface_kind="density",
    isovalue=0.02,
)

For CLI or archive workflows, use a JSONL manifest with one frame object per line. Paths in JSONL are resolved relative to the manifest file. As an import convenience, extxyz trajectories can store per-frame surface paths in Atoms.info["surface_path"].

Multi-Isovalue Shells

from aseview import SurfaceViewer

viewer = SurfaceViewer(
    "HOMO.cube",
    surface_kind="orbital",
    isovalue="0.03:0.09:4",
    surface_color="#3b82f6",
    positive_color="#ef4444",
    negative_color="#2563eb",
    opacity_ramp=[0.25, 0.45, 0.65, 0.8],
)
viewer.show()

For unsigned density surfaces, each parsed isovalue entry creates one shell using surface_color. For signed orbital and delta-density surfaces, each level creates positive and negative shells using positive_color and negative_color; the same opacity_ramp is applied to both phases.

v1 Scope

SurfaceViewer v1 intentionally excludes ESP/NCI workflows: no ESP colormaps, ESP colorbars, NCI modes, NCI color controls, solvent surfaces, or VS Code .cube editor integration are implemented. Use it for cube-backed orbital, density, and two-cube delta-density isosurfaces in Python, Jupyter, and saved HTML.