Renderer features
Read and change how the chart is drawn at runtime, with no indicator re-run.
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 drawA 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:
| Feature | Type | Default | Notes |
|---|---|---|---|
logScale | boolean | false | Logarithmic price scale on the price pane. |
currentPriceLine | boolean | true | The dashed line at the latest price. The axis label is a separate feature — see priceLabel (native renderer). |
upColor | color string | #089981 | Bullish candle body/wick color. |
downColor | color string | #f23645 | Bearish 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
| Feature | Type | Default | Notes |
|---|---|---|---|
glow | number (0 – ~0.7) | 0 | Neon 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.) |
priceBaseline | number | null | null | Reference price for priceStyle: 'baseline'. null derives it from the config's baseline.baselineLevel (a percent of the visible pane range). |
baselinePrice | number (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. |
candleZOrder | number | 0 | Draw-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. |
highlights | HighlightArea[] | [] | 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 } | null | null | Session 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 on | Strategy 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
| Feature | Type | Default | Notes |
|---|---|---|---|
animZoom | number (ms) | boolean | 70 | Eased 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. |
animPan | number (ms) | boolean | 110 | Pan 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). |
animScroll | number (ms) | boolean | 130 | The 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. |
animAutoscale | number (ms) | boolean | 80 | The 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. |
animLiveBar | number (ms) | boolean | 0 | Glide 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. |
axisDrag | boolean | true | Drag 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. |
paneResize | boolean | true | Drag the separator between panes to resize them; double-clicking a separator restores the two adjacent panes to an even split. |
keyboard | boolean | true | Keyboard 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. |
historyChords | boolean | true | The 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
0key.
Axes & scale
| Feature | Type | Default | Notes |
|---|---|---|---|
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. |
timezone | IANA zone string | 'UTC' | Time zone for the time-axis ticks and crosshair / data-window stamps, e.g. 'America/New_York'. |
gridlines | boolean | true | Master toggle for the background gridlines (per-axis visibility/colors live in the rich config). |
axisLabels | boolean | true | Draw the price/time axis tick labels. |
priceLabel | boolean | true | The last-price axis tag. Independent of currentPriceLine — either can show without the other. |
countdown | boolean | true | The bar-close countdown tag next to the price axis. |
crosshairOverride | { vertical?, horizontal?, color?, width?, style?, opacity?, shadeRight? } | null | null | A 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. |
drawingsInteractive | boolean | true | Whether 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 | null | null | Your 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. |
autoScale | boolean | true | Whether 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
| Feature | Type | Default | Notes |
|---|---|---|---|
attribution | boolean | true | The 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). |
settings | boolean | false | An in-chart gear button + dialog to edit a curated slice of the rich config (colors, fonts, scale, timezone) with export/import. |
marks | boolean or { visible?, groups? } | everything on | The 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, notchart.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 patchThe 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.