Live Line Chart
Real-time streaming line chart with smooth scroll animation for live data feeds
Installation
Dependencies
Examples
Heartrate
"use client"
import { useCallback, useEffect, useRef, useState } from "react"
import LiveLineChart, {
LiveLine,
LiveXAxis,
LiveYAxis,
type LiveLinePoint,
} from "@/components/vritti/live-line-chart"
function useHeartRateData(intervalMs: number) {
const [data, setData] = useState<LiveLinePoint[]>([])
const [value, setValue] = useState(72)
const startRef = useRef(Date.now())
useEffect(() => {
const now = Date.now() / 1000
const seed: LiveLinePoint[] = []
for (let i = 59; i >= 0; i--) {
seed.push({
time: now - i * (intervalMs / 1000),
value: 68 + Math.random() * 14,
})
}
setData(seed)
startRef.current = Date.now() - 30000 // start mid-warmup
}, [intervalMs])
useEffect(() => {
const id = setInterval(() => {
const elapsedSec = (Date.now() - startRef.current) / 1000
const cycle = elapsedSec % 120 // 2-minute workout cycle
let target: number
if (cycle < 30) {
// warmup: 68 → 85 BPM
target = 68 + (cycle / 30) * 17
} else if (cycle < 90) {
// cardio: 140 → 178 BPM
const t = (cycle - 30) / 60
target = 140 + t * 38
} else {
// cooldown: 118 → 72 BPM
const t = (cycle - 90) / 30
target = 118 - t * 46
}
const bpm = Math.round(target + (Math.random() - 0.5) * 8)
const now = Date.now() / 1000
setValue(bpm)
setData((prev) => [
...prev.filter((p) => p.time >= now - 120),
{ time: now, value: bpm },
])
}, intervalMs)
return () => clearInterval(id)
}, [intervalMs])
return { data, value }
}
export function LiveLineChartHeartRateDemo() {
const { data, value } = useHeartRateData(500)
const formatBpm = useCallback((v: number) => `${Math.round(v)} BPM`, [])
return (
<div className="w-full p-4">
<LiveLineChart
data={data}
value={value}
window={60}
margin={{ top: 16, right: 16, bottom: 40, left: 72 }}
style={{ height: 260 }}
>
<LiveLine
dataKey="value"
stroke="hsl(0 84% 60%)"
formatValue={formatBpm}
/>
<LiveXAxis />
<LiveYAxis formatValue={formatBpm} position="left" />
</LiveLineChart>
</div>
)
}
Price
"use client"
import { useCallback, useEffect, useRef, useState } from "react"
import LiveLineChart, {
LiveLine,
LiveXAxis,
LiveYAxis,
type LiveLinePoint,
} from "@/components/vritti/live-line-chart"
function useLiveData(initialPrice: number, intervalMs: number) {
const [data, setData] = useState<LiveLinePoint[]>([])
const [value, setValue] = useState(initialPrice)
const priceRef = useRef(initialPrice)
const momentumRef = useRef(0)
useEffect(() => {
const nowSec = Date.now() / 1000
const seed: LiveLinePoint[] = []
let p = initialPrice
let mom = 0
for (let i = 30; i > 0; i--) {
mom = mom * 0.92 + (Math.random() - 0.48) * 0.012
p *= 1 + mom
p = Math.max(p, 1)
seed.push({
time: nowSec - i * (intervalMs / 1000),
value: Math.round(p * 100) / 100,
})
}
priceRef.current = p
momentumRef.current = mom
setData(seed)
setValue(p)
}, [initialPrice, intervalMs])
useEffect(() => {
const id = setInterval(() => {
momentumRef.current =
momentumRef.current * 0.88 + (Math.random() - 0.48) * 0.008
momentumRef.current *= 0.995
priceRef.current *= 1 + momentumRef.current
priceRef.current = Math.max(priceRef.current, 1)
const rounded = Math.round(priceRef.current * 100) / 100
setData((prev) => {
const cutoff = Date.now() / 1000 - 60
return [
...prev.filter((p) => p.time >= cutoff),
{ time: Date.now() / 1000, value: rounded },
]
})
setValue(rounded)
}, intervalMs)
return () => clearInterval(id)
}, [intervalMs])
return { data, value }
}
export function LiveLineChartPriceDemo() {
const { data, value } = useLiveData(142.5, 600)
const formatUsd = useCallback((v: number) => `$${v.toFixed(2)}`, [])
return (
<div className="w-full p-4">
<LiveLineChart
data={data}
margin={{ top: 16, right: 16, bottom: 40, left: 56 }}
style={{ height: 260 }}
value={value}
window={30}
>
<LiveLine
dataKey="value"
formatValue={formatUsd}
stroke="var(--chart-line-primary)"
/>
<LiveXAxis />
<LiveYAxis formatValue={formatUsd} position="left" />
</LiveLineChart>
</div>
)
}
Server
"use client"
import { useEffect, useRef, useState } from "react"
import LiveLineChart, {
LiveLine,
LiveXAxis,
LiveYAxis,
type LiveLinePoint,
} from "@/components/vritti/live-line-chart"
type ServerPoint = { time: number; cpu: number; memory: number }
function useLiveServerData(intervalMs: number) {
const [data, setData] = useState<ServerPoint[]>([])
const [lastCpu, setLastCpu] = useState(35)
const cpuRef = useRef(35)
const memRef = useRef(58)
const tickRef = useRef(0)
useEffect(() => {
const now = Date.now() / 1000
const seed: ServerPoint[] = []
let cpu = 35
let mem = 58
for (let i = 29; i >= 0; i--) {
cpu = Math.max(5, Math.min(95, cpu + (Math.random() - 0.5) * 8))
mem = Math.max(40, Math.min(85, mem + (Math.random() - 0.5) * 3))
seed.push({ time: now - i * (intervalMs / 1000), cpu, memory: mem })
}
cpuRef.current = cpu
memRef.current = mem
setData(seed)
}, [intervalMs])
useEffect(() => {
const id = setInterval(() => {
tickRef.current++
// simulate CPU spike every ~10 seconds
if (tickRef.current % 12 === 0) {
cpuRef.current = Math.min(95, cpuRef.current + 35 + Math.random() * 20)
} else {
cpuRef.current = Math.max(5, cpuRef.current * 0.85 + Math.random() * 8)
}
memRef.current = Math.max(
40,
Math.min(85, memRef.current + (Math.random() - 0.48) * 2),
)
const now = Date.now() / 1000
const cpu = Math.round(cpuRef.current * 10) / 10
const point: ServerPoint = {
time: now,
cpu,
memory: Math.round(memRef.current * 10) / 10,
}
setLastCpu(cpu)
setData((prev) => [
...prev.filter((p) => p.time >= now - 60),
point,
])
}, intervalMs)
return () => clearInterval(id)
}, [intervalMs])
return { data, lastCpu }
}
export function LiveLineChartServerDemo() {
const { data, lastCpu } = useLiveServerData(800)
const formatPct = (v: number) => `${v.toFixed(1)}%`
return (
<div className="w-full p-4">
<LiveLineChart
data={data as unknown as LiveLinePoint[]}
value={lastCpu}
window={30}
margin={{ top: 16, right: 16, bottom: 40, left: 56 }}
style={{ height: 260 }}
>
<LiveLine
dataKey="cpu"
stroke="var(--chart-1)"
formatValue={formatPct}
/>
<LiveLine
dataKey="memory"
stroke="var(--chart-2)"
formatValue={formatPct}
/>
<LiveXAxis />
<LiveYAxis formatValue={formatPct} position="left" />
</LiveLineChart>
</div>
)
}