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

# BezierCurve API


 Bases: `AnyWidget`


Interactive arbitrary-degree Bezier curve editor.


Double-click to add control points, drag points to reshape the curve, and use the play controls to advance `t` from 0 to 1. Coordinates synced to Python use the configurable data bounds rather than SVG pixels.



```
from wigglystuff import BezierCurve

from wigglystuff import BezierCurve

curve = BezierCurve(closed=True, loop=True)
curve
```


Create a BezierCurve widget.


  Source code in `wigglystuff/bezier_curve.py`

```
def __init__(
    self,
    points: Iterable[dict[str, Any]] | None = None,
    *,
    closed: bool = False,
    playing: bool = False,
    loop: bool = False,
    t: float = 0.0,
    x_bounds: tuple[float, float] = (0.0, 1.0),
    y_bounds: tuple[float, float] = (0.0, 1.0),
    width: int = 600,
    height: int = 360,
    interval_ms: int = 30,
    duration_ms: int = 12000,
    sync_throttle_ms: int = 250,
    **kwargs: Any,
) -> None:
    """Create a BezierCurve widget.

    Args:
        points: Initial control points as ``{"x": float, "y": float}``.
        closed: Whether to append the first point virtually for rendering.
        playing: Whether playback starts immediately.
        loop: Whether playback wraps from ``t=1`` back to ``t=0``.
        t: Initial curve parameter in ``[0, 1]``.
        x_bounds: Data-coordinate x bounds.
        y_bounds: Data-coordinate y bounds.
        width: SVG width in pixels.
        height: SVG height in pixels.
        interval_ms: Milliseconds between browser playback ticks.
        duration_ms: Milliseconds for one full ``t=0`` to ``t=1`` traversal.
        sync_throttle_ms: Minimum milliseconds between playback syncs.
        **kwargs: Forwarded to ``anywidget.AnyWidget``.
    """
    coerced_points = _coerce_points(points)
    initial_t = _clamp01(t)
    initial_x, initial_y = _de_casteljau(
        _effective_points(coerced_points, closed), initial_t
    )
    super().__init__(
        points=coerced_points,
        x=initial_x,
        y=initial_y,
        t=initial_t,
        closed=closed,
        playing=playing,
        loop=loop,
        x_bounds=x_bounds,
        y_bounds=y_bounds,
        width=width,
        height=height,
        interval_ms=interval_ms,
        duration_ms=duration_ms,
        sync_throttle_ms=sync_throttle_ms,
        **kwargs,
    )
    self.observe(self._refresh_current_point, names=["points", "t", "closed"])
```


## current_point


```
current_point() -> tuple[float, float]
```


Return the current Bezier point at `t` as `(x, y)`.

 Source code in `wigglystuff/bezier_curve.py`

```
def current_point(self) -> tuple[float, float]:
    """Return the current Bezier point at ``t`` as ``(x, y)``."""
    return _de_casteljau(_effective_points(self.points, self.closed), self.t)
```


## sample


```
sample(n: int = 100) -> list[dict[str, float]]
```


Sample `n` points along the current Bezier curve.

 Source code in `wigglystuff/bezier_curve.py`

```
def sample(self, n: int = 100) -> list[dict[str, float]]:
    """Sample ``n`` points along the current Bezier curve."""
    if n <= 0:
        raise ValueError("n must be positive.")
    points = _effective_points(self.points, self.closed)
    if not points:
        return []
    if n == 1:
        x, y = _de_casteljau(points, 0.0)
        return [{"x": x, "y": y}]
    return [
        {"x": x, "y": y}
        for x, y in (
            _de_casteljau(points, index / (n - 1)) for index in range(n)
        )
    ]
```


## Synced traitlets


| Traitlet | Type | Notes |
| --- | --- | --- |
| `points` | `list[dict]` | Control points as `{"x": float, "y": float}` in data coordinates. |
| `x` | `float` | Current Bezier point x-coordinate at `t`. |
| `y` | `float` | Current Bezier point y-coordinate at `t`. |
| `t` | `float` | Curve parameter, clamped to `[0, 1]`. |
| `closed` | `bool` | Whether to virtually append the first point so the curve returns to the start. |
| `playing` | `bool` | Whether playback is currently advancing `t`. |
| `loop` | `bool` | Whether playback wraps from `t=1` to `t=0`. |
| `interval_ms` | `int` | Milliseconds between browser playback ticks. |
| `duration_ms` | `int` | Milliseconds for one full `t=0` to `t=1` traversal. |
| `sync_throttle_ms` | `int` | Minimum milliseconds between playback updates synced to Python. |
| `x_bounds` | `tuple[float, float]` | Data-coordinate x bounds. |
| `y_bounds` | `tuple[float, float]` | Data-coordinate y bounds. |
| `width` | `int` | SVG width in pixels. |
| `height` | `int` | SVG height in pixels. |


## Helper methods


| Method | Returns | Description |
| --- | --- | --- |
| `current_point()` | `tuple[float, float]` | Current Bezier point at `t`. |
| `sample(n=100)` | `list[dict]` | `n` sampled points along the curve. |
