SPB Git

spb/earth-now Public License

earth-now.co — real-time planetary dashboard: live world metrics modeled, not streamed.

TypeScript 93% Shell 2.3% SQL 1.4% JavaScript 1.3% Dockerfile 1.2% CSS 0.8%
2.0 KB · 58 lines typescript
Raw Blame History
1/**2 * earth-now.co3 * Author:  Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File:    apps/web/lib/smoothing.ts6 * Purpose: Pure 60 s linear blend between an old and a new CounterModel — no visible jump when SSE delivers a refit (time is a parameter)7 */89import { counterValue, type CounterModel } from "@earth-now/counter";1011/** Guardrail duration: a model swap is smoothed over 60 s (CLAUDE.md production rule). */12export const SMOOTHING_DURATION_MS = 60_000;1314/** Blend weight for the new model at time t: 0 before swapStart, 1 after swapStart+duration. */15export function smoothingAlpha(16  tMs: number,17  swapStartMs: number,18  durationMs: number = SMOOTHING_DURATION_MS,19): number {20  if (durationMs <= 0 || tMs >= swapStartMs + durationMs) return 1;21  if (tMs <= swapStartMs) return 0;22  return (tMs - swapStartMs) / durationMs;23}2425/** Linear blend of two already-evaluated values across the smoothing window. */26export function blendValues(27  oldValue: number,28  newValue: number,29  tMs: number,30  swapStartMs: number,31  durationMs: number = SMOOTHING_DURATION_MS,32): number {33  const alpha = smoothingAlpha(tMs, swapStartMs, durationMs);34  if (alpha === 0) return oldValue;35  if (alpha === 1) return newValue;36  return oldValue * (1 - alpha) + newValue * alpha;37}3839/**40 * Evaluator that linearly cross-fades counterValue(oldModel) → counterValue(newModel)41 * over [swapStartMs, swapStartMs + durationMs]. Exactly the old value at swapStart,42 * exactly the new value from swapStart+duration onwards. Pure — never reads the clock.43 */44export function makeSmoothedEvaluator(45  oldModel: CounterModel,46  newModel: CounterModel,47  swapStartMs: number,48  durationMs: number = SMOOTHING_DURATION_MS,49): (tMs: number) => number {50  return (tMs: number): number => {51    const alpha = smoothingAlpha(tMs, swapStartMs, durationMs);52    if (alpha === 1) return counterValue(newModel, tMs);53    const oldValue = counterValue(oldModel, tMs);54    if (alpha === 0) return oldValue;55    return oldValue * (1 - alpha) + counterValue(newModel, tMs) * alpha;56  };57}58