render_pythreejs

render_pythreejs class

Reactively render a pythreejs 3D scene.

Usage Source

render_pythreejs(
    _fn: Optional[ValueFn[ValueT]] = None,
    *,
    width: Optional[str] = None,
    height: Optional[str] = None,
    fill: Optional[bool] = None,
    fillable: Optional[bool] = None,
    rate_policy: Optional[RatePolicy] = None,
    rate_delay: Optional[float] = None,
)

@render_pythreejs is a decorator that creates a render function – a function that returns the scene to display and re-runs when the reactive values it reads change:

import pythreejs as p3

from shinywidgets import render_pythreejs


@render_pythreejs
def scene():
    mesh = p3.Mesh(p3.BoxGeometry(1, 1, 1), p3.MeshBasicMaterial(color="#1f77b4"))
    camera = p3.PerspectiveCamera(position=[2, 2, 2])
    return p3.Renderer(
        scene=p3.Scene(children=[mesh]),
        camera=camera,
        controls=[p3.OrbitControls(controlling=camera)],
    )

The render function should return a pythreejs.Renderer widget (i.e., a Scene plus a camera, with meshes/lights added to the scene; requires pip install 'shinywidgets[pythreejs]'). By default, the scene fills its container (the canvas and camera aspect resize with it); give the Renderer an explicit width or height (or a layout height) to use a fixed pixel size instead.

Note

Accessing the rendered widget: the render function gains two read-only attributes: .value, the value it most recently returned, and .widget, the rendered Renderer instance. Widget state can also be read reactively with .read("some_trait") (a shortcut for reactive_read() on .widget).

Responding to user interaction: scene.camera() reads the live camera after every orbit, much like input.my_slider() reads an input. Call it in a reactive function (a @render.* function, @reactive.calc, or @reactive.effect), which then re-runs whenever the camera changes. Until the first orbit it reports the Python-side camera as constructed (pythreejs doesn’t sync it at mount). For a single trait, reactive_read(scene.widget.camera, "position") is the per-trait alternative. With pythreejs.OrbitControls (or TrackballControls), the camera’s position, quaternion, and zoom sync back to Python each time the user finishes orbiting.

Mutating the widget: use .widget to update the rendered scene in place; trait changes on the widget are synced to the browser.

Parameters

width : Optional[str] = None

A CSS length unit (e.g., "400px" or "100%") for the output’s width.

height : Optional[str] = None

A CSS length unit for the output’s height. When None (the default), the output is designed to fill the height of its parent container (see fill and fillable).

fill : Optional[bool] = None

Whether the output should grow or shrink to fit its parent container. When None (the default), this is True if height is not specified.

fillable : Optional[bool] = None

Whether the output container should fill available space (scrolling its contents on overflow). When None (the default), this is True if height is not specified.

rate_policy : Optional[RatePolicy] = None

How to rate-limit reactive_read() on the widget: "debounce" or "throttle". None (the default) debounces reactive_read() only if rate_delay is given; no accessor on this renderer declares a default.

rate_delay : Optional[float] = None

The rate-limit window in seconds (default 0.25). 0 disables rate limiting.

Notes

  • Scene background: set pythreejs.Scene(background=...) (it defaults to white and overrides Renderer(background=...)).
  • Give pythreejs.DirectionalLight/pythreejs.SpotLight an explicit target (added to the scene) and shadow (with a camera) – pythreejs creates defaults for them in the browser otherwise, and browser-created widgets live until the session ends, so every re-render would leak 3 models per light (and log a transient console error).
  • Rotate objects via mesh.quaternion; pythreejs’s rotation (Euler) updates don’t render (an upstream pythreejs bug).

out.read("camera") also works, but returns the raw camera snapshot tuple; prefer camera(), which returns the live Camera object.

See also

output_widget(): in Shiny Core, the UI needs this to hold the output (Shiny Express displays it where the render function is defined).

Methods

camera() The scene’s live pythreejs.Camera (a PerspectiveCamera by default).

camera()

render_pythreejs.camera()

The scene’s live pythreejs.Camera (a PerspectiveCamera by default).

Readers are invalidated when its position/quaternion/zoom change (at the end of each orbit) or the camera is replaced. Reading before the first render cancels silently (like req(False)). Until the first orbit, this is the Python-side camera as constructed (pythreejs doesn’t sync it at mount). The returned object is the same mutable instance across reads – copy what you keep. Not rate-limited (the browser syncs once per orbit).