Vela, our next-generation charting platform, is live at vela.luxalgo.com.
LuxAlgo

Renderer features

Read and change how the chart is drawn at runtime, with no indicator re-run.

View as Markdown

chart.renderer is the control surface for the active renderer — read and change how the chart is drawn at runtime, with no indicator re-run.

chart.renderer.set('logScale', true); // one feature
chart.renderer.set({ upColor: '#fff', glow: 0.6 }); // several at once → one repaint
chart.renderer.get('logScale'); // read the current value
chart.renderer.supports('glow'); // is it available on this renderer?
chart.renderer.name; // 'native'
chart.renderer.capabilities; // what the renderer can draw

A key the active renderer doesn't support emits a console warning and is ignored — the chart is never touched. Use supports() to check first (e.g. to hide a UI control on a renderer that lacks the feature).

These are the same keys you can pass at construction (as options); setting them through chart.renderer.set applies them live instead of rebuilding the chart — so toggling them never re-executes your indicators.

Common features

Available on every renderer:

FeatureTypeDefaultNotes
logScalebooleanfalseLogarithmic price scale on the price pane.
currentPriceLinebooleantrueThe dashed line at the latest price. The axis label is a separate feature — see priceLabel (native renderer).
upColorcolor string#089981Bullish candle body/wick color.
downColorcolor string#f23645Bearish candle body/wick color.

Native renderer

name === 'native'. Supports every common feature, plus its own. They group into appearance, interaction, axes & scale, and in-chart UI.

Appearance

FeatureTypeDefaultNotes
glownumber (0 – ~0.7)0Neon glow/bloom on line series. WebGL2 only — the canvas2d backend stores the value but draws no glow.
priceStyle'candles' | 'bars' | 'line' | 'area' | 'baseline''candles'How the base price series is drawn. (Heikin Ashi is not yet available.)
priceBaselinenumber | nullnullReference price for priceStyle: 'baseline'. null derives it from the config's baseline.baselineLevel (a percent of the visible pane range).
baselinePricenumber (read-only)—The RESOLVED baseline reference price the paint splits on: priceBaseline when set, else the level% of the price pane's current range. For host chrome that colors by baseline position (e.g. a status line's value ink). Writes are ignored.
candleZOrdernumber0Draw-order key of the price candles relative to overlay indicators. Indicators default to z ≥ 1, so candles sit behind all overlays by default.
seriesOrder{ id, to: 'front' | 'back' } or { id, z }—Reorder one indicator's series layer — move it to front/back, or set an explicit z key.
highlightsHighlightArea[][]Shaded vertical time bands (session highlighting, e.g. weekends or pre/regular/post), drawn behind grid + data. Malformed entries are dropped; bands are sorted by start time.
sessionZones{ pre, post, extended } | nullnullSession time bands ([start, end) epoch-ms pairs per phase), shaded behind grid + data with the config's sessions.premarketColor / sessions.postmarketColor / sessions.extendedColor. Markets with a same-day pre/post split populate pre/post; markets whose extended session wraps midnight (an evening open rolling into the next day) populate the single extended phase instead. A host derives them from its market calendar (the widget does this automatically on markets with sessions); null means the market has no session structure.
tradeMarkers{ visible?, labels?, qty?, colors? }everything onStrategy trade markers (the order-fill arrows a strategy indicator emits via IndicatorModel.trades). Partial merge: visible hides the units, labels the order-id line, qty the signed-quantity line; colors overrides { long, short, exit } (defaults #2962ff / #f23645 / #d500f9). Malformed fields are dropped.

Interaction

FeatureTypeDefaultNotes
animZoomnumber (ms) | boolean70Eased wheel-zoom: the bar spacing glides toward each notch's target. 0/false = instant; true = the 70 ms default; a number = the ease time-constant in ms (visually settled in about three times that; capped at 1000). Reads back as a number. Takes effect on the next interaction. Off, the widget's keyboard zoom keys jump too.
animPannumber (ms) | boolean110Pan momentum: the velocity a drag releases with decays over this time-constant. 0/false = the chart stops dead on release. Reads back as a number. The umbrella for pan motion — setting it also switches animScroll on or off (at that glide's own duration).
animScrollnumber (ms) | boolean130The programmatic scroll glide — the scroll-to-latest button, panBy, the keyboard pan keys — easing the view toward its target at constant zoom. 0/false = those pans apply instantly. Reads back as a number.
animAutoscalenumber (ms) | boolean80The price scale's glide toward its new range while a zoom or fling is in flight. 0/false = it snaps every frame. Reads back as a number.
animLiveBarnumber (ms) | boolean0Glide of the forming bar on live ticks: the displayed high/low/close (and the current-price line and label) ease toward each new value instead of snapping. 0/false = snap; true = the 90 ms default; a number = the ease duration in ms (visually settled in about three times that; capped at 1000). Reads back as a number. A new bar always snaps; the crosshair, legend and data window always show the real values. Takes effect on the next tick.
intro'settle' | 'grow' | { style?, duration? } | false'settle' (650 ms)Reveal animation on first paint; the object form also sets the sweep duration in ms (capped at 5000). Reads back as the style (false when off). Setting it replays the intro (handy for comparing styles from the console).
zoomAnchor'right' | 'cursor''right'Wheel-zoom anchor: pin the right edge / latest bar, or the bar under the cursor. Affects the next wheel-zoom. Holding Shift (or a horizontal/trackpad swipe) makes the wheel pan through history instead of zooming.
axisDragbooleantrueDrag the right price-axis strip to rescale vertically and the bottom time-axis strip to zoom horizontally; scrolling the wheel over the price-axis strip rescales the same way, gently (scroll up compresses the span, down expands it); double-clicking an axis strip resets it.
paneResizebooleantrueDrag the separator between panes to resize them; double-clicking a separator restores the two adjacent panes to an even split.
keyboardbooleantrueKeyboard navigation/accessibility: focusable chart with arrow-key crosshair stepping (Shift+Arrow pans), Alt+Shift+→ scrolls back to the latest bars at the current zoom, +/- zoom, Home/End jump, 0 reset (fit content), Escape clear, plus ARIA labels and a live region. Ctrl/Cmd chords are left untouched for the host's own shortcuts (the widget's pan/zoom glides, the browser's Ctrl+0, …). When the latest bar is scrolled off-screen, a proximity-revealed » button in the bottom-right corner does the same.
historyChordsbooleantrueThe drawings layer answers Ctrl/Cmd+Z / Y itself (drawing undo/redo). A host that owns a unified history — drawings plus its own app actions in one timeline, like the widget — sets it to false so the chords bubble up to the host's keymap instead of being consumed in-chart. Copy/paste/duplicate/delete/nudge keys are unaffected.

The anim* features and intro are what the animations option resolves to (see Options). The rich config carries only their on/off state — animations.{zoom, pan, autoscale, intro} and priceScale.animateLastPrice, the rows of the settings dialog's Symbol → Animation group; turning one on there reuses the last non-zero duration set through the option or the feature.

Double-click behavior changed. Double-clicking the chart data area no longer fits the content to the view. Instead it maximizes the double-clicked pane so it fills the chart and every other pane is fully hidden: double-clicking the price pane hides all study panes, and double-clicking a study pane hides the price pane (and any other studies). A second double-click on the maximized pane restores the previous layout. Double-click on an axis strip (reset that axis) and on a pane separator (even split) is unchanged. To fit content the way the old data-area double-click did, press the 0 key.

Axes & scale

FeatureTypeDefaultNotes
scaleMode'price' | 'percent''price'Price-axis display: absolute price, or percent change vs the first visible bar. Gridlines, axis labels, and the crosshair chip all follow it.
timezoneIANA zone string'UTC'Time zone for the time-axis ticks and crosshair / data-window stamps, e.g. 'America/New_York'.
gridlinesbooleantrueMaster toggle for the background gridlines (per-axis visibility/colors live in the rich config).
axisLabelsbooleantrueDraw the price/time axis tick labels.
priceLabelbooleantrueThe last-price axis tag. Independent of currentPriceLine — either can show without the other.
countdownbooleantrueThe bar-close countdown tag next to the price axis.
crosshairOverride{ vertical?, horizontal?, color?, width?, style?, opacity?, shadeRight? } | nullnullA temporary restyle of the crosshair for interactions that ask the user to pick a point on the chart. Each field replaces the configured crosshair style while set; horizontal: false (or vertical: false) hides that line and its axis tag. shadeRight: { color, opacity? } veils the plot right of the bar under the vertical line (every pane, up to the price scale) — e.g. to hide what follows a time being picked. It styles and veils an external (synced) crosshair the same way (renderer.setExternalCrosshair), so a pick spanning several charts reads the same on each. Runtime-only: never part of getConfig(), so a reload can't leave it stuck. null restores the configured crosshair. The veiled area also carries a styleable element — see Styling the shadeRight veil.
drawingsInteractivebooleantrueWhether the pointer reaches the user drawings. Set it to false while an interaction asks the user to pick a point on the chart (usually alongside a crosshairOverride): a press over a drawing, or with a drawing tool armed, then pans or clicks like one on the empty plot and reaches onClick, so nothing is selected, moved, placed or erased, and hovering shows no drawing handles. A drawing gesture already under way finishes first. The drawings still show, and chart.drawings keeps working. Runtime-only: never part of getConfig(), so a reload can't leave the drawings inert. true (or null) restores it.
priceAxisTicks(ctx) => PriceAxisTick[] | null | nullnullYour own price-axis ticks: called per pane, it returns { price, label, major? } entries that replace the built-in ladder — the axis labels and the horizontal gridlines together. null/undefined from the function keeps the built-in ticks; major: true draws a semibold label, major: false a muted one. Seeded by the priceAxis.ticks option, which documents the context fields. It runs again only when a pane's range, height or mode changes — set the function again to recompute. Runtime-only: never part of getConfig(). null restores the built-in ticks.
autoScalebooleantrueWhether the price pane auto-scales to fit visible data. Setting it to false freezes the current window (unlocking vertical price pan/drag); setting it to true drops the freeze and resumes autoscale.

Styling the shadeRight veil

While a crosshairOverride with shadeRight paints its veil, the chart also places an empty element with the class vela-shade-right over exactly the veiled area: from the right edge of the picked bar to the price scale, across the full plot height. It sits below the crosshair layer and above the candles, indicators and drawings, so a backdrop-filter on it reaches the bars under the veil but never the crosshair line or the veil color itself:

.vela-shade-right { backdrop-filter: grayscale(1); }
chart.renderer.set('crosshairOverride', {
  horizontal: false,
  shadeRight: { color: 'rgba(0, 0, 0, 0)' }, // a transparent veil: grey bars, no darkening
});

The element is transparent unless you style it, ignores the pointer, is hidden from assistive technology, and carries the hidden attribute whenever no veil is painted. It follows a synced crosshair's veil too.

In-chart UI

FeatureTypeDefaultNotes
attributionbooleantrueThe in-chart attribution mark (bottom-left logomark linking to the Vela™ project). Disabling it is allowed only when an equivalent visible attribution is displayed elsewhere on the page (see the repository's NOTICE file).
settingsbooleanfalseAn in-chart gear button + dialog to edit a curated slice of the rich config (colors, fonts, scale, timezone) with export/import.
marksboolean or { visible?, groups? }everything onThe timeline-mark lane above the time axis. false hides the lane; { groups: { dividends: false } } hides one group — the same switch as the Events tab's checkboxes, persisted with the config. Partial merge; malformed fields drop.

Pane controls. Hovering a pane reveals a small button cluster in its top-right corner: move the pane up/down, collapse/expand it, and maximize/restore it. Each indicator's legend row also carries a Move to control for moving or merging it into another pane — merging gives the moved indicator its own price-scale column to the right of the pane's scale, autoscaled independently. This is a native-renderer capability (capabilities.paneManagement); on a renderer that lacks it the pane operations warn and no-op.

The drawing toolbar is also in-chart UI, but it lives on its own facade — chart.drawings, not chart.renderer — and is shown by default. It adds its own keyboard shortcuts (undo/redo, copy/paste/duplicate, delete, nudge) on top of the navigation keys above.

Screenshot export

chart.renderer.screenshot() returns a PNG data URL of the current chart (or null on a renderer that doesn't support it, with a warning). It composites the canvas layers in the order you see them: the series geometry — with any drawings stacked among the series already inside it — then the chrome layer that carries script-drawn shapes, then the drawings that sit over everything. The crosshair, the DOM overlays (tables, legend, data window) and the volume-profile layer are not included.

const url = chart.renderer.screenshot();
if (url) { const a = document.createElement('a'); a.href = url; a.download = 'chart.png'; a.click(); }

Data-window readout

chart.renderer.dataWindowReadout() returns the bar under the crosshair — or the latest bar when the cursor is off the plot — as a ready-to-display snapshot: the timestamp split into date and time, the ohlc block (with vol when the bar carries volume, and up telling you which way to tint it), and one groups entry per indicator holding a row per plot with its own color. Every number is already formatted on the scale of the pane it belongs to, so a panel built on it does no formatting of its own.

Pair it with onCrosshairMove to keep your own readout in step; it returns null on a renderer that doesn't provide one. This is what the workspace's data window is built from.

chart.renderer.onCrosshairMove(() => {
    const readout = chart.renderer.dataWindowReadout();
    if (!readout) return;
    console.log(readout.date, readout.time, readout.ohlc?.c);
    for (const group of readout.groups) console.log(group.name, group.rows);
});

Rich config (templates / persistence)

chart.renderer.getConfig() returns the renderer's full cosmetics as a serializable, versioned JSON document — every inherited value is resolved to a concrete one, so an exported template stands on its own. Feed it back with applyConfig() to restore saved settings or load a template. Untrusted/partial JSON is validated and merged, so malformed fields are dropped and a partial patch changes only what it names. No indicator re-run.

const template = chart.renderer.getConfig(); // snapshot
localStorage.setItem('chartConfig', JSON.stringify(template));

chart.renderer.applyConfig(JSON.parse(localStorage.getItem('chartConfig'))); // restore
chart.renderer.applyConfig({ candles: { upColor: '#26a69a' } }); // partial patch

The covered cosmetics include layout (background, text, font), grid colors/visibility, crosshair (color/width/style/opacity/label), price scale (mode, log, border, labels, current-price line, last-price animation on/off), the animation switches (zoom, pan momentum, price-scale glide, first-load reveal), time-scale timezone, candle border/wick, and per-style colors for bars / line / area / baseline.

Custom renderers

The native renderer is the only bundled backend, but the multi-renderer port stays: a custom IChartRenderer class passed as options.renderer declares its own features list, and every chart.renderer call degrades gracefully (unsupported keys warn and no-op). See adding a renderer.

On this page