Overview
Introduction
The Script Editor allows you to write custom indicator overlays directly in JavaScript that execute dynamically during the chart rendering loop. Indicators have direct access to standard 2D canvas drawing APIs, mouse interactions, indicator feeds, and viewport boundaries.
Execution Flow
Whenever the chart renders due to user panning, zooming, resizing, or incoming ticks, your custom indicator code is executed from start to finish. The script engine evaluates standard JavaScript immediately, passing a sandboxed proxy parameter named ctx.
Safety & Sandboxing boundaries
To prevent layouts and drawing corruption, script execution is wrapped inside a safe evaluation shell. Drawings are automatically clipped to the main price chart grid so they cannot bleed over the horizontal time scale or vertical price scale axes.
Coordinate Mapping
Drawing on a canvas requires pixel coordinates. The system provides two primary helper conversion methods on the ctx context:
ctx.barToX(i): Converts a bar indexi(from0tobars.length - 1) to its respective X pixel coordinate on the canvas.ctx.priceToY(p): Converts a numeric price valuep(e.g.1.2345) to its respective Y pixel coordinate on the canvas.
State Preservation
Because scripts execute repeatedly on every UI draw call, understanding scope is critical:
- Persistent State (var): Declaring variables in the global block scope (which translates to outer declarations in the transpiler) preserves their values across render cycles. Use these to store rolling totals, win/loss stats, or active drawing references.
- Loop State: Variables defined inside the main bar execution loop are re-initialized on each bar iteration and do not persist across bars or render cycles.
Exposing Technical Indicators & Volume
Your custom indicator scripts have access to standard technical indicator values in two distinct ways:
- Built-in Math Helpers: Call standard math functions directly from
ctx(such asctx.sma(closes, 20),ctx.rsi(closes, 14), orctx.macd(closes)) to calculate indicators dynamically. - Active Indicators Array: Inspect
ctx.indicatorsto read pre-calculated arrays for active indicators running on the chart. Every indicator object contains a populated.valuesproperty. - Accessing Volume: Retrieve raw bar volume using
ctx.bars[i].volume, or get the entire history array viactx.vol(ctx.bars).
Rendering Optimization & 60 FPS Guidelines
To ensure high-performance scripting (60 FPS rendering without lag or stutter):
- Clamp rendering loops: Restrict canvas rendering iterations from
Math.max(0, Math.floor(bounds.startIndex) - 1)toMath.ceil(bounds.endIndex) + 1. This padding ensures indicators pan smoothly without abruptly disappearing. Avoid looping through the entire historical dataset. - Function Hoisting (CRITICAL): Never declare helper functions inside loops or main rendering callbacks. Hoist all user-defined functions to the global scope to prevent heavy memory allocation.
- Local Scoping: Scope loop-local variables with block-level
letorconstto prevent global namespace lookups and scope leaks. - Map Callback Dereferencing: Avoid allocating new array references or parsing objects inside map callbacks. Dereference precalculated indicator arrays directly using index keys.
- Execution Limits: Scripts taking longer than 12ms to execute will trigger a performance warning in the console.
Mouse Interactions
You can query the real-time mouse position and action states using the ctx.mouse object. This allows you to build interactive hover overlays, highlight target coordinates, or reveal tooltip stats under the cursor.