Script outputs: line, histogram, bands, markers, levels, table, panel
A user script returns an array of outputs, each identified by a shape. Five shapes are drawn on the canvas (line, histogram, bands, markers, levels) and two are rendered as draggable DOM cards (table, panel). Every output is normalised before it reaches the renderer, and anything unrecognised is reported rather than silently dropped.
Senzoukria · Documentation · Updated September 2026
Where to find it
- Where
- Return value of compute() in any user script; drawn on the chart when the script is shown
- Targets
- "On the chart" (overlay) or "Own pane" (a 90 px pane under the chart)
- DOM limits
- Table: 50 rows and 8 columns; panel: 50 entries; excess is truncated
- Time key
- point.time must equal a bar's bucketTsNs (nanosecond epoch); other points are discarded
What it does
The renderer only knows SeriesOutput values; it never sees the script that produced them. The built-in indicators and user scripts therefore share the same shapes and the same normalisation rules: a point outside the visible bars is discarded, a non-finite value becomes a gap (value: null), and a null value written on purpose, for example during a moving average's warm-up, is preserved as a lifted pen instead of a false segment.
The draw target is chosen per script. "On the chart" overlays the price; "Own pane" reserves a pane whose height is fixed by the renderer at 90 px. Because tables and panels are DOM elements, a pane script whose only remaining outputs are a table does not reserve an empty pane: the split between canvas and DOM outputs is made before the renderer counts panes.
Shapes and their fields
| Shape | Fields | Defaults applied |
|---|---|---|
| line | points [{time, value|null}], style {color, width, style} | color #7ed321; width clamped 0.5–12; style solid, dashed or dots |
| histogram | points [{time, value}], positiveColor, negativeColor | #22c55e positive, #ef4444 negative; a bar without a finite value emits no point |
| bands | upper, basis, lower point arrays, fillAlpha, style | fillAlpha 0.12, clamped 0–1; style as for line |
| markers | markers [{time, price, kind, color, label?}] | kind dot unless arrow-up, arrow-down or square; color #7ed321 |
| levels | levels [{price, from, to|null, color, label?, style?}] | to null = runs to the right edge; only price and from are required |
| table | title?, columns [{key, label, align?}], rows [{key: string|number|null}] | Rejected when no column; max 8 columns and 50 rows; unknown row keys dropped |
| panel | title?, entries [{label, value, color?}] | Max 50 entries; an entry without a string or finite number value is skipped |
Table and panel cards
Tables and panels are rendered as floating cards over the chart because a table has to be read, selected and copied, which a canvas cannot offer, and because a 90 px pane holds only three lines. Each card is titled with the script name, followed by " · " and the output's title when one is given. Cards are dragged by their header; their position is saved per output under the key orderflow.seriesTables.v1 at the end of the drag. Without a saved position, cards cascade from the top-right corner (60 px down and 12 px in, offset by 26 px and 14 px per additional card) so two cards never overlap exactly.
The DOM is updated once per animation frame, not at the 4 Hz bar publication rate, and a null cell is printed as "—" rather than 0. Column alignment defaults to left; a script that wants numbers on the right sets align: "right" on the column.
How to use it
The lib helpers build the common shapes: lib.line(chronoBars, values, color, width), lib.histogram, lib.markers, lib.levels, lib.table(columns, rows, title) and lib.panel. Arrays passed to lib helpers are in chronological order, so reverse the newest-first bars first. A script can return several outputs, for example one bands output and one line for a VWAP with sigma bands.
A rejected shape does not vanish: the script is flagged in the Indicators panel, where the line under its name becomes "Unrecognised return value. An indicator returns a series, lines or markers — see \"Indicator: what to return\"." instead of its draw target. A script that returns outputs which draw nothing is flagged "empty", which distinguishes "it does not work" from "there was nothing to show".
Limits and pitfalls
- Widths below 0.5 or above 12 px are clamped; unknown style names fall back to solid.
- Histogram points with a null or non-finite value are removed entirely, unlike lines where null is a gap.
- Levels are segments keyed by price and from; they are not filtered on bar time, so a from timestamp older than the loaded history simply starts at the left edge.
- A table with more than 50 rows is truncated; write the truncation in the title yourself if the count matters.
Related pages
- User indicator editor on the chart
- C++ scripts (WASM engine)
- VWAP explained
- Session VWAP bands indicator
This page in other languages
Frequently asked questions
- Why is my table not in the pane I selected?
- Tables and panels always render as DOM cards over the chart, whatever the draw target. The target only affects canvas shapes such as lines and histograms.
- How do I show a gap instead of a line to zero during warm-up?
- Return value: null for those points. The renderer lifts the pen; a zero would draw a false segment.
- Can I move the table card and keep its place?
- Yes. Drag it by its header; the position is stored per output in localStorage and restored when the chart reopens.