LuxAlgo

Drawing tools

Interactive drawing tools, the toolbar, and the chart.drawings API.

View as Markdown

Vela™ ships an interactive drawing-tools layer: a docked toolbar and ~67 on-chart tools — trend lines, channels, Fibonacci, harmonic patterns, annotations, and more — that a user draws, edits, and keeps on the chart. Drawings are anchored in data space (time + price), so they stay locked to the bars across pan, zoom, timeframe changes, and reload — the same durability a scripting engine's own drawing output gets.

The toolbar is on by default on the native renderer. Everything it does is also reachable from code through the chart.drawings surface, and the whole model persists to JSON.

Drawing tools vs script drawings. This page is about the interactive tools a person places on the chart. They are distinct from the lines/boxes/labels an indicator emits as output — those are part of the neutral model and are covered under indicators.


The toolbar

A flush vertical bar docked in a gutter on the left of the plot (the chart insets to its right, so the bar never overlaps candles, the legend, or the axes).

  • Tool groups. Each group is a single cell showing its last-used tool's icon. Click the icon to arm that tool. Multi-tool groups also show a small arrow on hover — click the arrow to open a flyout listing every tool in the group; pick one to arm it (and it becomes the group's new last-used icon). Click the arrow again to close the flyout.
  • Cursor. The top button returns to select/idle (no tool armed).
  • Modes (bottom of the bar) — renderer-local, mutually exclusive with each other and with any armed tool:
    • Measure — a transient ruler (click–move–click) that reports the price/%/bar delta. It is not saved as a drawing; it clears on the next press, pan, or zoom. Shift+click an empty spot starts a measurement right there, no toolbar trip needed (press-drag-release works too). The magnet snaps both endpoints; a right-click or Escape cancels an in-progress measurement and returns to the pointer (no context menu for that click).
    • Eraser — click a drawing to delete it, or press-and-drag across several to wipe them.
    • Magnet — a 3-state snap toggle: off → weak → strong. Strong always snaps a new drawing anchor or measure-ruler endpoint to the nearest candle's time + OHLC; weak snaps only when a candle point is within a few pixels of the cursor. Holding Ctrl/Cmd is a momentary strong override.
    • Stay in drawing mode — an on/off toggle (pen with a lock, under the magnet). When on, finishing a drawing leaves the tool armed so you can keep placing the same tool without re-picking it; when off, most tools disarm after one placement (the brush family always stays armed either way). Click the cursor button or press Escape to return to select/idle.
  • Tooltips. Hovering any control briefly shows a small label beside it; a tool group's tooltip names the tool its icon will arm (the group's last-used one).
  • Favorites. Every tool row in a flyout carries a star at its right edge (revealed on row hover, gold when set). Starring is a user preference, not document data: the set survives symbol/timeframe rebuilds, is persisted by the shell alongside the other UI state (persist), and is readable and writable from code — see below. Hosts and plugins can build their own UI on top of it (a favorites bar, a radial picker…).
  • Shortcut hints. When the host binds a keyboard shortcut that arms or places a tool, the flyout row shows the chord beside the star (the shell binds Alt+T for the trend line and Alt+H / Alt+V for lines at the cursor out of the box). Hints are pushed as display strings via chart.drawings.setToolShortcuts({ trendline: 'Alt+T', … }), so they always match the host's actual bindings and platform formatting.

Arm a tool, then click to place its anchors (most tools, including shapes, are click-then-move-then-click; freehand/brush is the exception and captures the drag path; the polyline takes any number of clicks and finishes on Enter or double-click).

Hold Shift for exact angles. On the segment line tools (trend line, ray, extended line, info line, trend angle, arrow), holding Shift after the first anchor rounds the line to the nearest 45° step as drawn on screen — horizontal, vertical, or a perfect diagonal. It works the same when dragging an endpoint of an existing line, and it temporarily overrides the magnet so the locked angle is kept exactly.

Editing a drawing

Select a drawing (click it) to show its handles and a compact quick-settings popup floating beside it. The popup is built from each tool's own schema, so it shows only the controls that tool supports — line color/width/style, fill, text, and (for Fibonacci tools) a gear panel to enable/recolor/label each level. The popup also locks, reorders (bring-to-front / send-to-back), and deletes the drawing.

Text is typed on the chart. Placing a text annotation opens a blinking caret at the click point next to an Enter Text placeholder, framed by a thin gray box that marks the text as being edited, and the glyphs appear exactly where they will be painted as you type, at the annotation's default large size. Enter starts a new line; a click elsewhere (or Ctrl/Cmd+Enter) keeps the text, and Escape restores what was there before — a text annotation that was never typed into is discarded rather than left invisible. Double-click existing text, or a callout, to reopen the same inline editor.

Finished text keeps that frame as its selection cue: a plain text annotation shows the same thin box when it is selected, and a fainter one while the cursor is over it — the words are the drawing, so the frame is what tells you where it can be clicked, dragged, or restyled.

The quick-settings popup opens alongside the caret, exactly as it does for every other tool. For annotations whose text is the drawing — text, note, callout, comment, signpost — the text color and size sit on the popup's bar, so restyling while you type is one click and the words on the chart follow immediately; reaching for them does not end the edit. Bold and italic live under the field behind the popup's Text button, next to the text they format. On shapes that merely carry a label (a trend line, a box), all four controls stay in that panel with the label field.

Drag a handle to reshape; drag the body to move the whole drawing.

Keyboard shortcuts

When a drawing is selected (or hovered), with focus on the chart:

ActionShortcut
UndoCtrl/Cmd + Z
RedoCtrl/Cmd + Shift + Z or Ctrl/Cmd + Y
Copy / Paste / DuplicateCtrl/Cmd + C / V / D
DeleteDelete or Backspace
NudgeArrow keys (1px; Shift + arrow = 10px)
Cancel placement / measure / clear selectionEscape

Shortcuts stand down while a text field (e.g. a label editor) is focused, so typing is never hijacked.

Three mouse shortcuts complement these, and need no selection first: middle-click a drawing to delete it, right-click while a drawing tool, the ruler, or the eraser is active — placing or merely armed — to cancel it and return to the pointer (even in stay-in-drawing-mode, and without opening the chart's context menu for that click; persistent toggles like the magnet and stay-in-drawing-mode itself are untouched), and Shift+click an empty spot to start the measure ruler at that point (the magnet still snaps the start).

Depth: anywhere in the stack

A new drawing starts just under the price — the candles read on top of it, the way they read on top of the indicators — so it behaves like annotation on the chart's background: a zone, a session band, a shaded area you see through the candles rather than across them. From there it can take any position in the pane's draw order: over everything, between two indicators, or at the very back. (The one exception is a tool whose body replaces the series pixels inside its area — the Magnifier's opaque inset — which starts above the whole stack instead, since under the candles its content would be buried; it can still be reordered afterwards.)

  • From the object tree. Each pane is one column, read top to bottom as front to back: its drawings, its indicators and (in the main pane) the price series, all together. Drag a drawing (or a group, which moves as one block) to any slot in that column and the chart repaints in that order.
  • From the context menu. Right-click a drawing row for Bring to front / Send to back — they clear the whole stack, candles and indicators included; a group's row offers the same for all of its members at once.
  • From code. The draw-order key is the drawing's zIndex, shared with the pane's series:
chart.drawings.add('box', { anchors }); // a new drawing starts just under the candles
chart.drawings.bringToFront(d.id); // over the whole stack
chart.drawings.sendToBack(d.id); // behind the candles and every indicator
chart.drawings.update(d.id, { zIndex: z }); // an exact slot among the series' keys

A drawing under the data stays fully interactive: it still hit-tests, and its selection handles draw on top, so you can always see and grab what you selected.

The position is part of the drawing, so it is saved with toJSON(), restored by fromJSON(), and undoable; the shell also persists the series' own order, so a saved chart comes back stacked as you left it. Depth needs a renderer that declares the drawingDepth capability (the native renderer does); where it is missing, drawings all paint over the data, zIndex orders only the drawings among themselves, and the tree keeps them in one block above the series.


Tool catalogue

67 tools across 9 groups. The Type key is the string you pass to chart.drawings.setTool('…') or chart.drawings.add('…'). Eraser, Magnet, Measure, and Stay in drawing mode are toolbar modes, not placeable types, so they have no key.

Lines

ToolType keyWhat it does
Trend LinetrendlineA segment between two points. An optional label follows the line's slope.
Horizontal LinehlineA full-width line at one price.
RayrayA line from a point, extended one way. An optional label follows the line's slope.
Extended LineextendedlineA line through two points, extended both ways. An optional label follows the line's slope.
Vertical LinevlineA full-height line at one time.
Horizontal RayhrayA horizontal line from a point, extended right.
Cross LinecrosslineA full-width + full-height cross through one point.
Info LineinfolineA segment with a readout of its price/%/bar delta. An optional label follows the line's slope.
Trend AngletrendangleA ray with its angle (°) labelled off the horizontal. An optional label follows the line's slope.

Channels

ToolType keyWhat it does
Parallel ChannelparallelchannelA baseline plus a parallel line offset by a third point; optional fill.
Disjoint ChanneldisjointchannelTwo independent segments forming a channel.
Flat Top/BottomflattopbottomA sloped baseline with a flat (constant-price) opposite side.

Pitchforks

ToolType keyWhat it does
PitchforkpitchforkAndrews' median line with parallel tines from three pivots.
Schiff PitchforkschiffpitchforkPitchfork with the handle origin shifted in price only.
Modified Schiff PitchforkmodifiedschiffpitchforkPitchfork with the origin at the full pivot midpoint.
Inside PitchforkinsidepitchforkPitchfork median anchored inside the pivots.

Shapes

ToolType keyWhat it does
RectangleboxAn axis-aligned filled box.
ArrowarrowA straight arrow between two points.
EllipseellipseA data-space ellipse in a bounding box.
TriangletriangleA three-point filled triangle.
PolylinepolylineA multi-point connected path (finish on Enter/double-click).
BrushfreehandA freehand stroke captured from the drag path.
CirclecircleA true pixel circle (round at any zoom) from center + edge.
Rotated RectanglerotatedrectA box rotated to a baseline plus a width.
PathpathA multi-point path with an end arrowhead.
ArcarcA half-ellipse dome over a chord.
CurvecurveA quadratic Bézier curve with an off-curve control point.
Arrow Mark UparrowmarkupA fixed-size up-arrow marker stamped at a point (green).
Arrow Mark DownarrowmarkdownA fixed-size down-arrow marker stamped at a point (red).

Annotations

ToolType keyWhat it does
TexttextA free text label pinned to a point.
CalloutcalloutA text box with a leader pointing at a target.
NotenoteFree text on a small rounded plate.
Price NotepricenoteA draggable box + leader that shows the pinned point's price.
CommentcommentA rounded speech balloon pointing at a target.
Price LabelpricelabelA pinned tag that auto-renders the price at its anchor.
SignpostsignpostA sign plate on a pole rising from a pinned level.

Stamps

ToolType keyWhat it does
FlagflagmarkA flag glyph stamped at a point (size + glyph editable).
IconiconstampA unicode icon stamp; the glyph is chosen from a picker.

Fibonacci & Gann

ToolType keyWhat it does
Fib RetracementfibretracementHorizontal retracement levels between two points.
Fib ExtensionfibextensionExtension levels projected from two points.
Trend-Based Fib ExtensionfibextensiontrendExtension levels from a three-point move.
Fib FanfibfanFan of rays at the Fibonacci ratios.
Fib Time ZonesfibtimezonesVertical lines at Fibonacci time intervals.
Fib ChannelfibchannelParallel Fibonacci levels along a sloped baseline.
Fib Speed Resistance FanfibspeedfanPrice + time rays subdividing a box at the ratios.
Trend-Based Fib TimetrendfibtimeVertical Fibonacci-time lines from a three-point move.
Fib CirclesfibcirclesConcentric circles at the Fibonacci ratios.
Fib Speed Resistance ArcsfibarcsConcentric semicircle arcs from a pivot.
Fib WedgefibwedgeFibonacci arcs between two rays from an apex.
Fib SpiralfibspiralA golden (φ) spiral.
Gann FangannfanA fan of Gann angles from a point.
Gann BoxgannboxA box gridded at Gann ratios.
Gann SquaregannsquareA Gann grid + angle fan + concentric arcs over a box.
Dedekind TessellationdedekindModular-group tiling of a user-defined time×price range (semicircles + verticals). Density via max curvature.
SonicsonicMach-1 wavefront figure: circles sized from a user-drawn diameter, piled into a perpendicular shock wall. Per-circle colors via the levels gear.
SupersonicsupersonicMach cone (M>1): same diameter-sized first circle, with a conical envelope. Mach number is adjustable. Per-circle colors via the levels gear.
Golden SonicgoldensonicSonic Mach figure whose circle radii follow Fibonacci ratios (including under 1: 0.236…0.786, then 1, φ, φ², …).
Golden SupersonicgoldensupersonicSupersonic Mach cone with the same Fibonacci radii (under-1 through extensions).

Patterns

ToolType keyWhat it does
XABCD PatternxabcdA 5-point labelled pattern with leg ratios.
ABCD PatternabcdA 4-point ABCD pattern.
Elliott Impulse Wave (1-5)elliottimpulseA 5-wave impulse labelled 1–5.
Elliott Correction Wave (ABC)elliottcorrectionA 3-wave correction labelled A–B–C.
Head & ShouldersheadshouldersA labelled head-and-shoulders with a neckline.
GartleygartleyHarmonic pattern; legs validated against Gartley's Fib bands.
BatbatHarmonic pattern validated against Bat ratios.
ButterflybutterflyHarmonic pattern validated against Butterfly ratios.
CrabcrabHarmonic pattern validated against Crab ratios.
SharksharkHarmonic pattern validated against Shark ratios.
CyphercypherHarmonic pattern validated against Cypher's (non-adjacent) ratios.

The harmonic patterns (Gartley…Cypher) draw a ✓/✗ badge and color each leg ratio green/red by whether it falls in that pattern's ideal Fibonacci band.

Forecast & Measure

ToolType keyWhat it does
Date & Price RangedatepricerangeA box reporting the time span + price/% change it covers.
Long/Short PositionpositionAn entry/stop/target box: click the entry, then drag in the profit direction (up for a long, down for a short). Shows risk:reward, percentages, dollar loss, and position size from your risk % and account balance (size is editable and back-solves the risk %). The gear panel has a long/short switch (mirrors the levels across the entry), exact level values in price or points, and per-label display toggles; zone colors and label styling sit on the quick bar.
MagnifiermagnifierA press-drag-release rectangle whose interior shows the same market at a lower timeframe, rendered in the chart's own price style and colors (candles, bars, line, area, Heikin Ashi; custom chart types fall back to candles) at true time/price positions. The timeframe chip on the rectangle's bottom-left corner is a dropdown — click it to switch — and the same pick leads the quick bar, beside the up/down color overrides and the border style; Auto subdivides the chart's timeframe, and both pickers offer only timeframes below the chart's own. The finer bars load in the background and follow live data; when the chart is already at the finest timeframe, or the area needs more bars than the tool will fetch, a notice inside the rectangle says so. Needs a market with a ranged data source (offline data arrays have nothing to fetch).

Driving drawings from code

Every chart exposes a chart.drawings control surface (a sibling of chart.renderer and chart.data). It is chainable, and the model is core-owned — so reading/serializing/undo work even on a renderer that can't paint drawings.

import { Vela } from '@luxalgo/vela';

const chart = new Vela('#chart', { data: bars });
await chart.ready();

// Arm a tool so the next clicks place it:
chart.drawings.setTool('trendline');

// …or place one directly (no clicking):
const d = chart.drawings.add('trendline', {
    paneId: 'price',
    anchors: [
        { time: bars[10].time, price: bars[10].low },
        { time: bars[40].time, price: bars[40].high },
    ],
});

chart.drawings.update(d.id, { style: { lineColor: '#ff9800', lineWidth: 2 } });
chart.drawings.lock(d.id);          // make it non-interactive
chart.drawings.showToolbar(false);  // hide the toolbar (drawings still work from code)

See the chart.drawings reference for the full method list and which methods are gated by renderer support.


Tool and mode state from code

The armed tool, the magnet, stay-in-drawing-mode, and the measure/eraser modes are all readable and drivable programmatically — the seam an external toolbar (e.g. a multi-chart workspace's shared bar) builds on:

chart.drawings.getTool();               // 'trendline' | … | null (select/idle)
chart.drawings.setSnapMode('strong');   // magnet: 'off' | 'weak' | 'strong'
chart.drawings.getSnapMode();
chart.drawings.setStayMode(true);       // keep the tool armed after each placement
chart.drawings.getStayMode();
chart.drawings.setMode('measure');      // 'measure' | 'eraser' | null (none)
chart.drawings.getMode();

// Follow every change, whatever its source (in-chart toolbar, keyboard, code):
chart.on('drawing:tool', ({ type }) => { /* armed tool changed; null = pointer */ });
chart.on('drawing:snap', ({ mode }) => { /* magnet mode changed */ });
chart.on('drawing:stay', ({ on }) => { /* stay-in-drawing-mode changed */ });
chart.on('drawing:mode', ({ mode }) => { /* measure/eraser entered or left */ });

The renderer keeps owning the mutual exclusion — arming a tool exits measure/eraser (and vice versa), and the outcome always lands on the events, so an external UI only ever mirrors. One-shot tools disarm themselves after placing (back to null on drawing:tool) unless stay-in-drawing-mode is on; the brush family always stays armed.

Favorite tools

chart.drawings.favorites();                     // ['trendline', 'box'] — starred, in star order
chart.drawings.isFavorite('trendline');         // true
chart.drawings.setFavorite('ray', true);        // star / unstar one type
chart.drawings.setFavorites(['ray', 'box']);    // replace the whole set (unknown types dropped)

chart.on('drawing:favorites', ({ favorites }) => { /* the set changed (star click or code) */ });

The shell persists the set with its other state when persist is enabled, and restores it on the next load.

Persistence, undo & clipboard

  • Save / restore. chart.drawings.toJSON() returns a versioned DrawingsDocument; pass it back to chart.drawings.fromJSON(doc) to restore. The restore path is lenient with untrusted input — malformed or unknown-type entries are dropped, never thrown. Drawings are not auto-persisted; storing the document is up to your app.
  • Undo / redo. chart.drawings.undo() / redo() (and canUndo() / canRedo()) walk a snapshot history. A multi-target action (multi-drag, multi-delete, duplicate, paste) is one undo step.
  • Clipboard. copyToClipboard(ids) + paste(), or duplicate(ids) / clone(id), mint fresh copies (new ids) and select them — an in-memory, per-chart clipboard.

Renderer support

Interactive drawing requires a renderer that declares the userDrawings capability — the native renderer does; a minimal custom adapter may not. When unsupported, chart.drawings.supported is false, the interactive methods warn and no-op, but the core-owned methods (add of the model, all, toJSON/fromJSON, undo/redo) still work, so a document round-trips even without an on-chart toolbar.

Stability: the drawing-tools API is young and may change before a 1.0 release. The persisted DrawingsDocument is versioned and migrated forward.

On this page