An editor for a breakpoint ramp, such as an ADSR shape or an automation curve.
Base UI
import { Envelope } from "@/components/audio/elements/envelope";
export function EnvelopeSizeDefaultDemo() {Edits a ramp made of breakpoints, such as an ADSR shape or an automation curve. The registry component wraps the Envelope primitive from @audio-ui/react and draws the points, the segments between them and a grid.
value is a list of { x, y, curve? } points. Both axes are normalized to 0…1, and the points are sorted by x. Map that range to your own unit, such as time or level.curve, from -1 to 1, bends the segment that leaves it. 0, or no value, draws a straight line. A positive curve holds the start of the segment and steepens its end; a negative curve does the opposite. The last point has no segment.minPoints (default 2, a start and an end) and maxPoints. Set both to the same number to fix the shape.snap sets the grid that points snap to, for example 0.25 for quarters. Hold Cmd/Ctrl to move off the grid. Without snap, points move freely. The registry component draws the grid.role="slider". The registry component names the points "Point n of N". With the primitive, set aria-label and getAriaValueText yourself.Import the primitive and assemble its parts.
import { Envelope } from "@audio-ui/react";
<Envelope.Root>
<Envelope.Graph>
<Envelope.Fill />
<Envelope.Line />
</Envelope.Graph>
<Envelope.Segments>
<Envelope.Segment />
</Envelope.Segments>
<Envelope.Points>
<Envelope.Point />
</Envelope.Points>
</Envelope.Root>import { Envelope } from "@/components/audio/envelope";Uncontrolled: set only the initial points.
<Envelope
defaultValue={[
{ x: 0, y: 0 },
{ x: 0.2, y: 1 },
{ x: 0.6, y: 0.5 },
{ x: 1, y: 0 },
]}
/>Controlled: drive the points from state.
const [points, setPoints] = useState(initialPoints);
<Envelope
onValueChange={setPoints}
onValueCommitted={(next, details) => console.log("Committed:", next, details.reason)}
value={points}
/>Behavior · 10
x stays between its neighbors and y within 0…1. Dragging never deletes a point. While dragging, the point follows the pointer even if a controlled value updates late.maxPoints. Added on a bent segment it sits on the curve, and each half is bent to keep its shape.minPoints.minPoints.onValueCommitted fires when a drag that moved a point ends, and right away for an add or a remove.A refused add or remove sets data-blocked for a moment on the graph or the point, which the registry styles turn into a short shake.
The points share one tab stop. On the focused point:
Keys · 4
y by stepx by step (clamped between neighbors)minPointsEach segment has a handle on its curve. The handles share one tab stop:
Keys · 4
Groups all parts of the breakpoint envelope and owns its points, each normalized to 0…1 on both axes and kept sorted by x. Points are dragged and nudged with the keyboard; a double click on the Graph adds one and a double click (or Delete) on a Point removes it, within minPoints / maxPoints. Renders a <div> element.
Props · 11
aria-valuetext.minPoints, the shape is fixed.0.25 snaps to quarters, 0 is no grid. Cmd/Ctrl leaves the grid and Shift's fine drag ignores it.Data attributes · 2
data-disableddata-draggingThe drawing surface, a 100×100 stretched viewBox: holds Line and Fill, and a double click on it adds a point. Carries data-blocked briefly when adding is refused at maxPoints. Renders a <svg> element.
Data attributes · 3
data-disableddata-draggingdata-blockedmaxPoints.The envelope curve through the points. Renders a <polyline> element.
Data attributes · 2
data-disableddata-draggingThe area under the curve. Renders a <polyline> element.
Data attributes · 2
data-disableddata-draggingRenders one child per point, in x order.
Props · 1
A draggable handle for the point at index, positioned with left/bottom percentages over the Graph. It is a role="slider" on the point's level (y): give it an aria-label. One point at a time is the tab stop; arrow keys move it (Shift for larger steps), Delete or a double click removes it. Carries data-blocked briefly when removal is refused at minPoints. Renders a <div> element.
Props · 2
aria-valuetext. Defaults to the formatted x, y.Data attributes · 4
data-disableddata-draggingdata-blockedminPoints.data-removableEnvelope accepts style props (size, className) plus primitive props from EnvelopePrimitive.Root.Props. aria-label / aria-labelledby name the envelope group.
Props · 9
x. curve, -1 to 1, bends the segment leaving the point.eventDetails.reason is "drag", "keyboard", "add", "remove" or "curve", and eventDetails.index is the affected point; call eventDetails.cancel() to reject it.Props · 6
xDomain={[0, 2000]}."ms" or "dB".@audio-ui/react/envelope exposes Envelope.Root, Envelope.Graph (the SVG surface; double-click to add), Envelope.Line and Envelope.Fill (the curve and the area under it), Envelope.Points (renders one child per point), Envelope.Point (the draggable handle), Envelope.Segments (renders one child per segment) and Envelope.Segment (the handle on a segment's midpoint, which bends it). Every part accepts a render prop and className / style as functions of state; points expose data-dragging, data-removable, data-blocked, data-disabled and data-index, and segment handles data-dragging, data-flat and data-disabled.
Base UI
"use client";
import { useState } from "react";