Pyodide Figure Library

2D rotation: SVG

This figure runs Python in your browser with Pyodide. NumPy computes the rotated vector; Figure2D creates SVG. Try the sliders, Play, and Reset, then open Show Python code.

The blue vector rotates in the xy plane. Negative angular velocity reverses its direction.

Loading Python example…

Show Python code
"""Draw the SO(2) rotation example with NumPy and Figure2D."""

import numpy as np
from figurekit import Figure2D


J = np.array([[0.0, -1.0], [1.0, 0.0]])
UNIT_X = np.array([1.0, 0.0])


def rotation_figure(omega: float, time: float) -> str:
    theta = omega * time
    rotation = np.eye(2) * np.cos(theta) + J * np.sin(theta)
    endpoint = rotation @ UNIT_X

    angles = np.linspace(0.0, theta, 121)
    trail = np.column_stack((np.cos(angles), np.sin(angles)))

    figure = Figure2D(x_range=(-1.35, 1.35), y_range=(-1.35, 1.35), id_prefix="rotation")
    figure.circle(0, 0, 1, class_="unit-circle")
    figure.line(-1.2, 0, 1.2, 0, class_="axis")
    figure.line(0, -1.2, 0, 1.2, class_="axis")
    figure.polyline(trail, class_="trail")
    figure.line(0, 0, 1, 0, class_="reference")
    figure.line(0, 0, endpoint[0], endpoint[1], class_="vector")
    figure.circle(endpoint[0], endpoint[1], 0.045, class_="endpoint", fill="#3996e6")
    figure.circle(0, 0, 0.025, class_="origin", fill="currentColor")
    figure.text("x", 1.15, -0.16)
    figure.text("y", 0.08, 1.14)
    return figure.to_json()

3D rotation: movable camera

Here NumPy rotates a unit vector about the (1, 1, 1) axis. Figure3D describes the geometry in a persistent 3D scene. Drag the figure to turn the camera, scroll to zoom, or focus it and use the arrow keys and +/-. Camera movement works while the simulation is playing. The orange line shows the vector’s trajectory.

Change the angular velocity and time, or press Play to animate the rotation.

Loading Python example…

Show Python code
"""Rotate a unit vector around the (1, 1, 1) axis in three dimensions."""

import numpy as np
from figurekit import Figure3D


AXIS = np.ones(3) / np.sqrt(3)
UNIT_X = np.array([1.0, 0.0, 0.0])


def rotate(angle):
    """Rodrigues' formula for rotation around AXIS."""
    return (
        UNIT_X * np.cos(angle)
        + np.cross(AXIS, UNIT_X) * np.sin(angle)
        + AXIS * np.dot(AXIS, UNIT_X) * (1 - np.cos(angle))
    )


def rotation3d_figure(omega: float, time: float) -> str:
    angle = omega * time
    endpoint = rotate(angle)
    trail = [rotate(value) for value in np.linspace(0, angle, 121)]

    figure = Figure3D(x_range=(-1.3, 1.3), y_range=(-1.3, 1.3), z_range=(-1.3, 1.3))
    figure.line((-1.3 * AXIS, 1.3 * AXIS), color="#888888", width=3, name="Rotation axis")
    figure.vector((0, 0, 0), UNIT_X, color="#888888", width=4, name="Initial vector")
    figure.line(trail, color="#dd8540", width=5, name="Trajectory")
    figure.vector((0, 0, 0), endpoint, name="Rotated vector")
    return figure.to_json()

Add a figure to an article

Put a Python source file and a JavaScript control script in the article’s page bundle, then call pyodide_demo with their URLs. A figure-specific stylesheet is optional. Zola displays the same Python source under Show Python code.

{% <pyodide_demo source="/blog/my-article/figure.py" script="/blog/my-article/figure.js" stylesheet="/blog/my-article/figure.css" title="Figure description"> %}
Instructions for the reader.
{% </pyodide_demo> %}

The Python source imports Figure2D or Figure3D from figurekit. Build the figure and return figure.to_json() from a function. The JavaScript script uses PyodideDemo.load(demo, { packages, functionName }) to get that function, then passes its result to PyodideDemo.render(demo, result). See the complete 2D Python, 2D controls, 3D Python, and 3D controls examples.

from figurekit import Figure3D

def make_figure():
    figure = Figure3D(x_range=(-2, 2), y_range=(-2, 2), z_range=(-2, 2), axis_font_size=16)
    figure.line([(0, 0, 0), (1, 1, 1)], color="#3996e6")
    figure.points([(1, 1, 1)])
    return figure.to_json()

Figure2D offers line(x1, y1, x2, y2), polyline(points), circle(x, y, radius), points(points), and text(label, x, y). Set x_range=(min, max) and y_range=(min, max) in its constructor to choose the visible bounds; omitted ranges default to a centered view. Coordinates use the usual mathematical y direction; the library handles SVG’s inverted y axis. Pass class_="name" to style SVG elements with CSS. It uses the locally stored drawsvg wheel, so include "drawsvg" in the loader’s packages list.

Figure3D offers line(points), points(points), vector(start, end), and surface(x, y, z). Points are (x, y, z) triples, and surfaces take matching 2D grids. Set x_range=(min, max), y_range=(min, max), or z_range=(min, max) in its constructor to fix individual axis limits; leave one out for automatic limits. Lines and points accept colors and widths or sizes. Surfaces support colorscale="Viridis", "Plasma", or "Gray". The browser updates geometry while preserving the camera and canvas. Figure3D needs no Python plotting package.

The site pins the Pyodide runtime and Three.js, which loads only for a 3D figure. A network connection is needed when a visitor first loads them. If the Python source lives outside content/, pass code_path="static/path/to/file.py" to the component so Zola can show it.

Set axis_font_size=16 on Figure3D to choose axis text size in pixels (default: 14). Axis names are slightly larger than numeric ticks, and text stays the same size when zooming.