Feeding a clanker? Grab this page as raw `.md` →

# Excalidraw API


 Bases: `AnyWidget`


An embedded Excalidraw whiteboard.


Draw shapes, arrows, text, and freehand sketches on an infinite canvas. The current drawing is kept in memory on the `scene` traitlet as an Excalidraw scene dict (`elements` / `appState` / `files`). Like the other drawing widgets, nothing is written to disk automatically — call :meth:`save` when you want to persist, and load with :meth:`from_file`.


  Example

```
import marimo as mo
from wigglystuff import Excalidraw

draw = mo.ui.anywidget(Excalidraw())
draw
```


After sketching something:


```
draw.save("diagram.excalidraw")          # write to disk
again = Excalidraw.from_file("diagram.excalidraw")  # load it back
```

  Source code in `wigglystuff/excalidraw.py`

```
def __init__(
    self,
    scene: Optional[dict] = None,
    height: int = DEFAULT_HEIGHT,
    sync_throttle_ms: int = 1000,
    theme: str = "light",
    **kwargs,
):
    super().__init__(**kwargs)
    self.height = height
    self.sync_throttle_ms = sync_throttle_ms
    self.theme = theme
    if scene is not None:
        self.scene = scene
```


## from_file `classmethod`


```
from_file(path: Union[str, Path], **kwargs) -> Excalidraw
```


Create an :class:`Excalidraw` preloaded with the scene at `path`.

 Source code in `wigglystuff/excalidraw.py`

```
@classmethod
def from_file(cls, path: Union[str, Path], **kwargs) -> "Excalidraw":
    """Create an :class:`Excalidraw` preloaded with the scene at ``path``."""
    scene = json.loads(Path(path).read_text(encoding="utf-8"))
    return cls(scene=scene, **kwargs)
```


## get_image_base64


```
get_image_base64() -> str
```


Return the current drawing as a PNG data URL (empty if nothing drawn).

 Source code in `wigglystuff/excalidraw.py`

```
def get_image_base64(self) -> str:
    """Return the current drawing as a PNG data URL (empty if nothing drawn)."""
    return self.image_base64
```


## get_pil


```
get_pil()
```


Return the current drawing as a PIL Image, or `None` if empty.


Handy for passing what you drew forward — e.g. into a multimodal model. The PNG is rendered in the browser and synced back, so it lags edits by up to `sync_throttle_ms`.

 Source code in `wigglystuff/excalidraw.py`

```
def get_pil(self):
    """Return the current drawing as a PIL Image, or ``None`` if empty.

    Handy for passing what you drew forward — e.g. into a multimodal model.
    The PNG is rendered in the browser and synced back, so it lags edits by
    up to ``sync_throttle_ms``.
    """
    if not self.image_base64:
        return None
    import base64
    import io

    from PIL import Image

    payload = self.image_base64.split(",", 1)[-1]
    return Image.open(io.BytesIO(base64.b64decode(payload)))
```


## get_scene


```
get_scene() -> dict
```


Return the current Excalidraw scene as a dict.

 Source code in `wigglystuff/excalidraw.py`

```
def get_scene(self) -> dict:
    """Return the current Excalidraw scene as a dict."""
    return dict(self.scene)
```


## save


```
save(path: Union[str, Path]) -> None
```


Write the current scene to `path` as a `.excalidraw` JSON file.

 Source code in `wigglystuff/excalidraw.py`

```
def save(self, path: Union[str, Path]) -> None:
    """Write the current scene to ``path`` as a ``.excalidraw`` JSON file."""
    Path(path).write_text(self.to_json(), encoding="utf-8")
```


## to_json


```
to_json() -> str
```


Return the current scene serialized as a JSON string.

 Source code in `wigglystuff/excalidraw.py`

```
def to_json(self) -> str:
    """Return the current scene serialized as a JSON string."""
    return json.dumps(self.scene)
```


## Synced traitlets


| Traitlet | Type | Notes |
| --- | --- | --- |
| `scene` | `dict` | Excalidraw scene (`elements` / `appState` / `files`). |
| `image_base64` | `str` | PNG data URL of the drawing; read via `get_pil()`. |
| `theme` | `str` | `"light"` (default) or `"dark"`; `""` follows the notebook theme. |
| `height` | `int` | Canvas height in pixels. |
| `sync_throttle_ms` | `int` | Minimum delay between syncing edits back to Python. |


Excalidraw itself is loaded from a CDN the first time the widget renders, so the widget needs network access and does not work fully offline.
