Skip to content

Drawing Tools

Drawing tools let users annotate the chart with lines and markers. They receive pointer events from the overlay canvas and render on every redraw.

Built-in Tools

TrendLine

Click two points on the candle panel to draw a line. Double-click to cancel a pending line.

ts
import { TrendLine } from 'chartgpu';

const trendLine = new TrendLine();
chart.addTool(trendLine);

// Activate
trendLine.active = true;
chart.activateTool(trendLine);

// Deactivate
trendLine.active = false;
chart.activateTool(null);

// Clear all lines
trendLine.clear();
PropertyDescription
activeWhether the tool accepts pointer input.
cursorReturns 'crosshair' when active, 'default' otherwise.

HorizontalRay

Double-click on the candle panel to place a horizontal price level. Double-click again near an existing level to remove it.

ts
import { HorizontalRay } from 'chartgpu';

const hRay = new HorizontalRay();
chart.addTool(hRay);

// Access placed levels
console.log(hRay.levels);  // number[]

Unlike TrendLine, HorizontalRay doesn't need to be explicitly activated — it responds to double-click at all times.

Custom Drawing Tools

Extend DrawingTool:

ts
import { DrawingTool, type DrawState } from 'chartgpu';

class FibRetracementTool extends DrawingTool {
  private points: { candleIdx: number; price: number }[] = [];
  active = false;

  get cursor() { return this.active ? 'crosshair' : 'default'; }

  onPointerDown(e: PointerEvent, state: DrawState): boolean {
    if (!this.active) return false;
    const { candles, viewport, section, chartWidth, paddingX, priceRange } = state;
    if (!priceRange) return false;

    const relX = (e.offsetX - paddingX) / chartWidth;
    const candleIdx = Math.round(viewport.start + relX * (viewport.end - viewport.start) - 0.5);
    const relY = (e.offsetY - section.top) / section.height;
    const price = priceRange.min + priceRange.range * (1 - relY);

    this.points.push({ candleIdx, price });

    if (this.points.length >= 2) {
      // Draw fib retracements between points[0] and points[1]
      // ... your logic
      this.points = [];
    }
    return true;
  }

  onPointerMove(_e: PointerEvent, _state: DrawState): boolean { return false; }
  onPointerUp(_e: PointerEvent, _state: DrawState): boolean   { return false; }
  onDblClick(_e: MouseEvent, _state: DrawState): boolean      { return false; }

  clear(): void { this.points = []; }

  draw(state: DrawState): void {
    // Render your tool using state.ctx
  }
}

Returning true from handlers

If a handler returns true, the event is consumed — the chart won't pan or zoom in response. Return false to let the chart handle it normally.

Released under the MIT License.