OHLC Bar Chart — Chart.js

An OHLC (Open-High-Low-Close) bar chart displays financial price data using vertical bars with horizontal tick marks. Each bar shows the price range from high to low as a thin vertical line, with a left tick indicating the opening price and a right tick indicating the closing price. Unlike candlestick charts that use colored bodies, OHLC bars provide a cleaner, less cluttered view favored by technical analysts who prefer to focus on price levels rather than visual patterns.

OHLC Bar Chart rendered with Chart.js

Renders

JavaScript source (Chart.js)

// anyplot.ai
// ohlc-bar: OHLC Bar Chart
// Library: chartjs 4.4.7 | JavaScript 22.23.2
// Quality: 91/100 | Created: 2026-09-02

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic) ----------------------------------------
// Tiny fixed-seed LCG — the browser has no seeded RNG.
function lcg(seed) {
  let state = seed;
  return () => {
    state = (state * 1664525 + 1013904223) % 4294967296;
    return state / 4294967296;
  };
}
const rand = lcg(7);

const sessionCount = 45;
const labels = [];
const opens = [];
const highs = [];
const lows = [];
const closes = [];
const barColors = [];

let price = 62; // crude oil futures, USD per barrel
const date = new Date(Date.UTC(2024, 2, 1));
for (let i = 0; i < sessionCount; i++) {
  while (date.getUTCDay() === 0 || date.getUTCDay() === 6) {
    date.setUTCDate(date.getUTCDate() + 1);
  }

  const open = price;
  const drift = (rand() - 0.48) * 1.6;
  const close = Math.max(open + drift, 1);
  const high = Math.max(open, close) + rand() * 0.9;
  const low = Math.min(open, close) - rand() * 0.9;
  const isUp = close >= open;

  labels.push(date.toLocaleDateString("en-US", { month: "short", day: "numeric" }));
  opens.push(open);
  highs.push(high);
  lows.push(low);
  closes.push(close);
  barColors.push(isUp ? t.palette[0] : t.palette[4]);

  price = close;
  date.setUTCDate(date.getUTCDate() + 1);
}

const priceMin = Math.min(...lows);
const priceMax = Math.max(...highs);
const pricePad = (priceMax - priceMin) * 0.12;

// --- Mount -------------------------------------------------------------------
const canvas = document.createElement("canvas");
document.getElementById("container").appendChild(canvas);

// --- Open/close tick + storytelling plugin -------------------------------------
// Draws the left (open) and right (close) horizontal ticks per bar, a small
// direction glyph (redundant to color, for CVD readers), and dashed period
// high/low reference lines — all directly on the canvas via core Chart.js
// plugin hooks. No external financial plugin.
const tickHalfWidth = 8;
const ohlcTicksPlugin = {
  id: "ohlcTicks",
  beforeDatasetsDraw(chart) {
    const { ctx, chartArea, scales } = chart;
    const highY = scales.y.getPixelForValue(priceMax);
    const lowY = scales.y.getPixelForValue(priceMin);

    ctx.save();
    ctx.lineWidth = 1;
    ctx.strokeStyle = t.inkSoft;
    ctx.setLineDash([5, 4]);
    ctx.beginPath();
    ctx.moveTo(chartArea.left, highY);
    ctx.lineTo(chartArea.right, highY);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(chartArea.left, lowY);
    ctx.lineTo(chartArea.right, lowY);
    ctx.stroke();
    ctx.setLineDash([]);

    ctx.fillStyle = t.inkSoft;
    ctx.font = "13px sans-serif";
    ctx.textAlign = "right";
    ctx.textBaseline = "bottom";
    ctx.fillText(`Period high: $${priceMax.toFixed(2)}`, chartArea.right - 6, highY - 4);
    ctx.textBaseline = "top";
    ctx.fillText(`Period low: $${priceMin.toFixed(2)}`, chartArea.right - 6, lowY + 4);
    ctx.restore();
  },
  afterDatasetsDraw(chart) {
    const { ctx, scales } = chart;
    ctx.save();
    ctx.lineWidth = 3;
    for (let i = 0; i < sessionCount; i++) {
      const x = scales.x.getPixelForValue(i);
      const openY = scales.y.getPixelForValue(opens[i]);
      const closeY = scales.y.getPixelForValue(closes[i]);
      const highY = scales.y.getPixelForValue(highs[i]);
      const lowY = scales.y.getPixelForValue(lows[i]);
      const isUp = closes[i] >= opens[i];
      ctx.strokeStyle = barColors[i];

      ctx.beginPath();
      ctx.moveTo(x - tickHalfWidth, openY);
      ctx.lineTo(x, openY);
      ctx.stroke();

      ctx.beginPath();
      ctx.moveTo(x, closeY);
      ctx.lineTo(x + tickHalfWidth, closeY);
      ctx.stroke();

      // Direction glyph — redundant (non-color) cue for CVD readers: a small
      // triangle above the high (up days) or below the low (down days),
      // pointing in the direction of the move.
      const glyphHalfWidth = 4;
      const glyphHeight = 7;
      const baseY = isUp ? highY - 4 : lowY + 4;
      const apexY = isUp ? baseY - glyphHeight : baseY + glyphHeight;
      ctx.beginPath();
      ctx.moveTo(x, apexY);
      ctx.lineTo(x - glyphHalfWidth, baseY);
      ctx.lineTo(x + glyphHalfWidth, baseY);
      ctx.closePath();
      ctx.fillStyle = barColors[i];
      ctx.fill();
      ctx.lineWidth = 1;
      ctx.strokeStyle = t.ink;
      ctx.stroke();
    }
    ctx.restore();
  },
};

// --- Chart -------------------------------------------------------------------
// Core Chart.js has no dedicated OHLC type, so the high-low range is a thin
// floating bar (one core "bar" dataset, data=[low, high]) and the open/close
// ticks are drawn with the plugin above. Only core Chart.js bar-chart and
// plugin-hook features are used — no chartjs-chart-financial or other package.
new Chart(canvas, {
  type: "bar",
  data: {
    labels,
    datasets: [
      {
        label: "Range",
        data: lows.map((low, i) => [low, highs[i]]),
        backgroundColor: barColors,
        borderWidth: 0,
        barThickness: 3,
      },
    ],
  },
  plugins: [ohlcTicksPlugin],
  options: {
    responsive: true,
    maintainAspectRatio: false,
    animation: false,
    plugins: {
      title: {
        display: true,
        text: "ohlc-bar · javascript · chartjs · anyplot.ai",
        color: t.ink,
        font: { size: 22 },
      },
      legend: {
        labels: {
          color: t.ink,
          font: { size: 16 },
          generateLabels: () => [
            { text: "Up ▲ (close ≥ open)", fillStyle: t.palette[0], strokeStyle: t.ink, lineWidth: 1 },
            { text: "Down ▼ (close < open)", fillStyle: t.palette[4], strokeStyle: t.ink, lineWidth: 1 },
          ],
        },
        onClick: () => {},
      },
      tooltip: {
        mode: "index",
        intersect: false,
        callbacks: {
          label: (item) => {
            const i = item.dataIndex;
            return `O: $${opens[i].toFixed(2)}  H: $${highs[i].toFixed(2)}  L: $${lows[i].toFixed(2)}  C: $${closes[i].toFixed(2)}`;
          },
        },
      },
    },
    scales: {
      x: {
        ticks: {
          color: t.inkSoft,
          font: { size: 14 },
          maxRotation: 45,
          minRotation: 45,
          autoSkip: true,
          maxTicksLimit: 14,
        },
        grid: { display: false },
        title: { display: true, text: "Date", color: t.ink, font: { size: 16 } },
      },
      y: {
        min: priceMin - pricePad,
        max: priceMax + pricePad,
        ticks: {
          color: t.inkSoft,
          font: { size: 14 },
          callback: (value) => "$" + value.toFixed(2),
        },
        grid: { color: t.grid },
        title: { display: true, text: "Price (USD/barrel)", color: t.ink, font: { size: 16 } },
      },
    },
  },
});

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/ohlc-bar/chartjs/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "ohlc-bar",
  "language": "javascript",
  "library": "chartjs",
  "page": "https://anyplot.ai/ohlc-bar/javascript/chartjs",
  "hub": "https://anyplot.ai/ohlc-bar",
  "code_json": "https://api.anyplot.ai/specs/ohlc-bar/chartjs/code",
  "spec_json": "https://api.anyplot.ai/specs/ohlc-bar",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/ohlc-bar/javascript/chartjs/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/ohlc-bar/javascript/chartjs/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/ohlc-bar/javascript/chartjs/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/ohlc-bar/javascript/chartjs/plot-dark.html",
  "quality_score": 91.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of OHLC Bar Chart on anyplot.ai.

Other implementations