[go: up one dir, main page]

Liveline

Liveline is a real-time animated line chart component for React. One <canvas>, no dependencies beyond React 18, smooth interpolation at 60fps.

Degen mode (chart shake and particles) with momentum arrows.

I built this because every charting library I tried was either too heavy for a simple live feed, or too rigid to feel alive. Liveline does one thing: draw a line that moves smoothly as new data arrives. Everything else is opt-in.


Getting started

npm install liveline

The component fills its parent container. Set a height on the wrapper.

import { Liveline } from 'liveline'
 
function Chart({ data, value }) {
  return (
    <div style={{ height: 200 }}>
      <Liveline data={data} value={value} />
    </div>
  )
}

data is an array of { time, value } points. value is the latest number.

Two props. That's it.

Feed it data however you like. WebSocket, polling, random walk. Liveline interpolates between updates so even infrequent data looks smooth. It works for anything with a value that changes over time.

Resting heart rate. Custom formatter, exaggerated Y-axis.


Momentum

The momentum prop adds directional arrows to the live dot. Green for up, red for down, grey for flat. Pass true to auto-detect direction, or force it with "up", "down", or "flat".

Arrows fade out fully before the new direction fades in.


Value overlay

showValue renders the current value as a large number over the chart. It updates at 60fps through direct DOM manipulation, not React re-renders. Pair it with valueMomentumColor to tint the number based on direction.

60fps value overlay with momentum colouring.


Time windows

Pass a windows array to render time horizon buttons. Each entry has a label and secs value. Three styles are available via windowStyle: "default", "rounded", and "text".

<Liveline
  windows={[
    { label: '1m', secs: 60 },
    { label: '5m', secs: 300 },
  ]}
  windowStyle="rounded"
/>

CPU usage with occasional spikes. Rounded time windows.


Reference line

referenceLine draws a horizontal line at a fixed value. Pass an object with value and an optional label.

Polymarket-style prediction line. "Will Bitcoin stay above $67,500?"


Orderbook

Pass an orderbook prop with bids and asks arrays to render streaming order labels behind the line. Each entry is a [price, size] tuple. Labels spawn at the bottom, drift upward, and fade out. Green for bids, red for asks. Bigger orders appear brighter.

The stream speed reacts to price momentum and orderbook churn (how much the bid/ask totals are changing). Calm markets drift slowly, volatile ones rush.

Kalshi-style orderbook stream. Bid and ask sizes float upward behind the price line.


Candlestick

Pass OHLC data to render candlesticks. The live candle keeps updating in real time, growing its wicks as new ticks arrive. A built-in toggle morphs between line and candlestick views.

Same data, two views. The toggle morphs between line and candlestick.


States

Real apps don't start with data ready. Set loading to show a breathing line animation while you wait for a connection. When data arrives, the flat line morphs smoothly into the actual chart. If data never arrives, Liveline falls back to a quiet empty state. Customise the message with emptyText.

Loading state, then data arrives. Loops every 9 seconds.

paused freezes the chart in place while data continues arriving in the background. When you resume, it catches up to real time.


Theming

Pass any CSS colour string to color and Liveline derives the full palette. Line, fill gradient, glow, badge, grid labels. It converts the input to HSL and generates every variant from there.

Dark theme. Same component, different colour.


More features

Everything is off by default or has sensible defaults. A few more things you can turn on:

  • exaggerate tightens the Y-axis range so small movements fill the full chart height. Useful for values that move in tiny increments, like the heart rate demo above.
  • scrub shows a crosshair with time and value tooltips on hover. On by default.
  • degen enables burst particles and chart shake on momentum swings. For when subtlety is not the goal.
  • badgeVariant="minimal" renders a quieter white pill instead of the accent-colored default. Or badge={false} to remove it entirely.

How it works

One <canvas>, one requestAnimationFrame loop. When a new value arrives, nothing jumps. The chart lerps toward the new state at 8% per frame (lerpSpeed). The Y-axis range, the badge, the grid labels all use the same lerp. The range snaps outward instantly when data exceeds it, so the line is never clipped. That's why it feels like one thing breathing rather than a bunch of parts updating independently.


Props

Data

dataLivelinePoint[]required
valuenumberrequired

Appearance

theme'light' | 'dark''dark'
colorstring'#3b82f6'
gridbooleantrue
badgebooleantrue
badgeVariant'default' | 'minimal''default'
badgeTailbooleantrue
fillbooleantrue
pulsebooleantrue

Features

momentumboolean | Momentumtrue
scrubbooleantrue
exaggeratebooleanfalse
showValuebooleanfalse
valueMomentumColorbooleanfalse
degenboolean | DegenOptionsfalse

State

loadingbooleanfalse
Breathing line animation while waiting for data
pausedbooleanfalse
Freeze chart scrolling; resume catches up to real time
emptyTextstring'No data to display'
Text shown when data is empty and not loading

Time

windownumber30
windowsWindowOption[]
onWindowChange(secs: number) => void
windowStyle'default' | 'rounded' | 'text'

Crosshair

tooltipYnumber14
tooltipOutlinebooleantrue

Orderbook

orderbookOrderbookData

Advanced

referenceLineReferenceLine
formatValue(v: number) => stringv.toFixed(2)
formatTime(t: number) => stringHH:MM:SS
lerpSpeednumber0.08
paddingPadding{ top: 12, right: 80, bottom: 28, left: 12 }
onHover(point: HoverPoint | null) => void
cursorstring'crosshair'
classNamestring
styleCSSProperties

Stress testing

A chart that only looks good on calm data isn't much use. These demos throw the worst stuff I could think of at it: wild volatility, sharp direction changes, isolated spikes on flat lines, and irregular data arrival with random gaps.

just because

Wild swings, fast updates (100ms)

Near-flat, ultra-low volatility, exaggerate (150ms)

Chaotic, huge spikes (80ms)

Sharp reversals are the classic breaking point. The first chart hammers the line with frequent direction changes at 60ms. The second holds nearly flat, then fires massive isolated spikes. The third is just chaos.

Frequent sharp reversals (60ms updates)

Near-flat with massive isolated spikes (120ms)

Rapid zigzag oscillation (50ms)

Real-world data doesn't arrive at regular intervals. WebSocket connections drop, batch updates land all at once, mobile networks stall. This one simulates that: long quiet stretches of 1-3 seconds between points, then sudden bursts at 40-80ms. The tick interval itself is random.


Just a line

Liveline can do a lot. Momentum arrows, particles, orderbooks, scrubbing, time windows. But at the end of the day, if you just want a line that moves when a number changes, it does that just fine too.