Getting started

Quickstart

Create an engine, add data and mount a chart, with a script tag or with npm and React.

Without a license key everything works and each chart shows a small “Powered by Kymoscope” badge; add your key when you have one, as licenseKey, or pass license.get to fetch fresh keys from your backend (see Automatic renewal).

Script tag

Serve kymoscope-embed.js, its assets/ folder and styles/charts.css (see Installation). React is bundled inside the script; your page does not need it.

index.html
<link rel="stylesheet" href="/vendor/kymoscope/styles/charts.css" />
<div id="chart" style="height: 360px"></div>

<script src="/vendor/kymoscope/kymoscope-embed.js"></script>
<script>
  const engine = Kymoscope.createEngine({ licenseKey: window.KYMOSCOPE_LICENSE_KEY })

  // 1. Data: numeric columns of equal length.
  const n = 2000
  const t = new Float64Array(n), rpm = new Float64Array(n)
  for (let i = 0; i < n; i++) {
    t[i] = i * 0.05
    rpm[i] = 6000 + 2500 * Math.sin(t[i] / 1.3)
  }
  engine.datasets.addColumns({ name: 'Run 42', columns: { t, rpm }, units: { t: 's', rpm: '1/min' } })

  // 2. A chart model in the active worksheet, with one series.
  const chartId = engine.charts.add('multiband', { title: 'RPM' })
  engine.charts.addSeries(chartId, 'rpm')

  // 3. A view of that chart in your element. It fills the element.
  const view = engine.charts.mount(chartId, document.getElementById('chart'))
</script>

The first numeric column (t) became the worksheet’s X axis. The chart has no toolbar or menu: the mouse wheel zooms it, and everything else (reset zoom, add a series, export) comes from your own buttons calling the engine.

npm and React

Analysis.tsx
import { useEffect, useState } from 'react'
import { createEngine, type KymoscopeEngine } from '@kymoscope/sdk'
import { Chart, KymoscopeProvider, useEngineState } from '@kymoscope/sdk/react'
import '@kymoscope/sdk/styles/charts.css'

export function Analysis({ run }: { run: { name: string; t: number[]; rpm: number[] } }) {
  const [engine, setEngine] = useState<KymoscopeEngine | null>(null)
  useEffect(() => {
    const e = createEngine({ licenseKey: import.meta.env.VITE_KYMOSCOPE_LICENSE_KEY })
    e.datasets.addColumns({ name: run.name, columns: { t: run.t, rpm: run.rpm } })
    void e.charts.addSeries(e.charts.add('multiband'), 'rpm')
    setEngine(e)
    return () => e.dispose()
  }, [run])

  if (!engine) return null
  return (
    <KymoscopeProvider engine={engine}>
      <Charts />
    </KymoscopeProvider>
  )
}

function Charts() {
  const charts = useEngineState((s) => s.worksheets.find((w) => w.id === s.activeWorksheetId)!.charts)
  return charts.map((c) => <Chart key={c.id} id={c.id} style={{ height: 320 }} />)
}

Creating the engine in an effect gives React StrictMode’s second mount a fresh engine and disposes the first. See React bindings.

Push a live signal

A local stream is fed by your page: no server and no encryption involved. It is plotted as soon as it opens.

TypeScript
const live = engine.live.openLocal('Rig A')
let t = 0
setInterval(() => {
  t += 1 / 30
  live.push({ t, signals: [{ name: 'load', unit: 'kN', value: 5 + 3 * Math.sin(t) }] })
}, 1000 / 30)

For data coming from devices, see Live streams.

Save what users build

TypeScript
// Worksheets and chart settings as JSON (datasets are not included).
engine.subscribe(debounce(() => {
  const { json } = engine.io.exportWorkspace()
  fetch('/api/workspaces/42', { method: 'PUT', body: JSON.stringify(json) })
}, 500))

// Later, in a new engine:
await engine.io.importWorkspace(await (await fetch('/api/workspaces/42')).json())

subscribe fires once per effective state change; debounce it yourself. See Import and export.