🚧 VeloxKit is pre-release software. APIs may change before v1.0. Get started →
Documentation
APIs & Bindings
perf

perf Stable WINMACLNX

Real-time frame-level performance metrics — frame time, JS time, layout time, GPU time, and memory usage. Works in both dev and production builds.

Import

import { perf } from 'veloxkit'

perf.snapshot()

Synchronously returns a snapshot of all current metrics.

const snap = perf.snapshot()

Returns PerfSnapshot | null

type PerfSnapshot = {
  fps:          number   // frames per second (rolling average)
  frameTime:    number   // average frame time in ms
  frameTimeP99: number   // 99th-percentile frame time (worst-case)
  jsTime:       number   // time spent in JS per frame (ms)
  layoutTime:   number   // time spent in Taffy layout (ms)
  gpuTime:      number   // GPU command submission time (ms)
  memoryJS:     number   // V8 heap used in MB
  nodeCount:    number   // number of live layout nodes
}

Returns null when the bindings are unavailable (e.g. in tests).


perf.onBudgetExceeded(cb, opts?)

Register a callback that fires whenever a frame exceeds a target time budget.

const unsub = perf.onBudgetExceeded((violation) => {
  console.warn('Slow frame:', violation)
  // → { budget: 16.667, actual: 24.3, jsTime: 18.1, layoutTime: 1.2 }
}, {
  target: 16.667,  // ms — default: 16.667 ms (60 fps)
})
 
// Remove listener:
unsub()

Returns an unsubscribe function.


perf.onLeakDetected(cb)

Register a callback for dev-mode memory / node-count leak warnings. Only fires in development builds — silently no-ops in production.

const unsub = perf.onLeakDetected((warning) => {
  console.warn('[leak]', warning.msg)
  // → { type: 'node_count', count: 342, msg: 'Node count growing steadily...' }
})

Examples

Live FPS display

import { perf } from 'veloxkit'
import { useState, useEffect } from 'react'
 
function FpsCounter() {
  const [fps, setFps] = useState(0)
 
  useEffect(() => {
    const id = setInterval(() => {
      const snap = perf.snapshot()
      if (snap) setFps(Math.round(snap.fps))
    }, 500)
    return () => clearInterval(id)
  }, [])
 
  return (
    <Text style={{
      position: 'absolute',
      top: 8, right: 8,
      fontSize: 12,
      color: fps >= 55 ? '#4ade80' : fps >= 30 ? '#facc15' : '#f87171',
    }}>
      {fps} fps
    </Text>
  )
}

Performance overlay (dev mode)

import { perf } from 'veloxkit'
import { useState, useEffect } from 'react'
 
function PerfOverlay() {
  const [snap, setSnap] = useState(null)
 
  useEffect(() => {
    const id = setInterval(() => setSnap(perf.snapshot()), 200)
    return () => clearInterval(id)
  }, [])
 
  if (!snap) return null
 
  return (
    <View style={{
      position: 'absolute', bottom: 0, left: 0, right: 0,
      backgroundColor: '#00000099',
      padding: '4px 8px',
      flexDirection: 'row',
      gap: 16,
    }}>
      {[
        ['FPS', snap.fps.toFixed(1)],
        ['Frame', `${snap.frameTime.toFixed(1)} ms`],
        ['P99', `${snap.frameTimeP99.toFixed(1)} ms`],
        ['JS', `${snap.jsTime.toFixed(1)} ms`],
        ['Nodes', snap.nodeCount],
        ['Mem', `${snap.memoryJS.toFixed(1)} MB`],
      ].map(([label, value]) => (
        <View key={label} style={{ alignItems: 'center' }}>
          <Text style={{ fontSize: 9, color: '#9999bb' }}>{label}</Text>
          <Text style={{ fontSize: 11, color: '#e7ecff' }}>{value}</Text>
        </View>
      ))}
    </View>
  )
}

Budget monitoring in production

// Log frame budget violations to crash reporter
perf.onBudgetExceeded((v) => {
  if (v.actual > 50) {  // only log severely slow frames (< 20 fps)
    crash.getReports()  // fire-and-forget; actual logging is in Rust
    console.error(`[perf] Frame ${v.actual.toFixed(1)}ms (budget ${v.budget}ms)`)
  }
}, { target: 16.667 })

perf.snapshot() is synchronous and costs ~1 µs — safe to call every frame or on every render without performance impact.