Browse Source

feat(viz): implement frame-rate independent smoothing for UI and sketches

Refactors animation and interpolation logic to ensure consistent behavior
across different monitor refresh rates (e.g., 60Hz vs 144Hz).

Changes:
- Implement time-delta adjusted smoothing for FPS counter and mouse tracking.
- Update IFT graph scaling in 'dom.js' to use performance-based delta time.
- Remove hardcoded 60FPS limit in zoom sketch to allow native refresh rates.
- Fix vertical tooltip jitter in 'drawUtils.js' by using smoothed mouse coordinates.
- Add 'lastOverlayUpdateTime' to global state for cross-module time tracking.
refactor/sync-centralize
RUSHIL AMBARISH KADU 3 months ago
parent
commit
76d2fb1d47
  1. 15
      steps/src/dom.js
  2. 20
      steps/src/p5/radarSketch.js
  3. 12
      steps/src/p5/zoomSketch.js
  4. 1
      steps/src/state.js

15
steps/src/dom.js

@ -126,6 +126,7 @@ export function resetUIForNewLoad(isNewVideo = true) {
// Reset the FPS counter state to prevent incorrect calculations on reload
appState.fps = 0;
appState.lastOverlayUpdateTime = 0;
// --- Conditional Video Reset ---
if (isNewVideo || !videoPlayer.src) {
@ -374,11 +375,21 @@ export function updatePersistentOverlays(currentMediaTime) {
targetMsPerBlock = Math.min(40, Math.max(10, maxWindowIFT / 10));
}
// --- START: Frame-Rate Independent Smoothing ---
// We use performance.now() to calculate a delta time for smooth animations
// across different monitor refresh rates.
const now = performance.now();
const dt = appState.lastOverlayUpdateTime ? now - appState.lastOverlayUpdateTime : 16.67;
appState.lastOverlayUpdateTime = now;
// Smooth Interpolation (Lerp)
// Move current scale towards the target.
// If playing, use 0.1 (fast). If stopped, use 0.033 (slow, ~3x slower).
const smoothingFactor = appState.isPlaying ? 0.1 : 0.033;
appState.currentGraphScale += (targetMsPerBlock - appState.currentGraphScale) * smoothingFactor;
const baseSmoothing = appState.isPlaying ? 0.1 : 0.033;
const adjustedSmoothing = 1 - Math.pow(1 - baseSmoothing, dt / (1000 / 60));
appState.currentGraphScale += (targetMsPerBlock - appState.currentGraphScale) * adjustedSmoothing;
// --- END: Frame-Rate Independent Smoothing ---
// If the scale hasn't converged yet and we are NOT playing (main loop not running),
// request another frame to continue the smoothing animation.

20
steps/src/p5/radarSketch.js

@ -209,8 +209,11 @@ export const radarSketch = function (p) {
if (framesDrawn === 10 || appState.fps === 0) {
appState.fps = currentFps;
} else {
const smoothingFactor = 0.95;
appState.fps = appState.fps * smoothingFactor + currentFps * (1 - smoothingFactor);
// --- START: Frame-Rate Independent FPS Smoothing ---
const baseFactor = 0.05; // Smoothing factor at 60 FPS
const adjustedFactor = 1 - Math.pow(1 - baseFactor, delta / (1000 / 60));
appState.fps = p.lerp(appState.fps, currentFps, adjustedFactor);
// --- END: Frame-Rate Independent FPS Smoothing ---
}
}
lastFrameTime = currentTime;
@ -375,13 +378,16 @@ export const radarSketch = function (p) {
isFirstFrame = false;
}
// The smoothing factor. A smaller value (e.g., 0.1) means more smoothing.
// This can be adjusted to feel more or less responsive.
const smoothingFactor = 0.5;
// --- START: Frame-Rate Independent Smoothing ---
// We use p.deltaTime to adjust the smoothing factor so that the animation
// speed remains consistent across different monitor refresh rates.
const baseSmoothing = 0.5; // Target smoothing at 60 FPS
const adjustedSmoothing = 1 - Math.pow(1 - baseSmoothing, p.deltaTime / (1000 / 60));
// Linearly interpolate the smoothed position towards the actual mouse position.
smoothedMouseX = p.lerp(smoothedMouseX, p.mouseX, smoothingFactor);
smoothedMouseY = p.lerp(smoothedMouseY, p.mouseY, smoothingFactor);
smoothedMouseX = p.lerp(smoothedMouseX, p.mouseX, adjustedSmoothing);
smoothedMouseY = p.lerp(smoothedMouseY, p.mouseY, adjustedSmoothing);
// --- END: Frame-Rate Independent Smoothing ---
// Use the smoothed coordinates for all subsequent zoom-related calculations.
const hoveredItems = handleCloseUpDisplay(p, plotScales, smoothedMouseX, smoothedMouseY);

12
steps/src/p5/zoomSketch.js

@ -219,7 +219,6 @@ export const zoomSketch = function (p) {
appState.zoomFactor = 4; // Set a default zoom factor in the global state
p.setup = function () {
p.frameRate(60);
// We enable looping so the lerp smoothing can animate between frames
p.loop();
};
@ -272,9 +271,14 @@ export const zoomSketch = function (p) {
smoothedAvgX = targetAvgX;
smoothedAvgY = targetAvgY;
} else {
const smoothingFactor = 0.05; // Tweak this for more/less lag
smoothedAvgX = p.lerp(smoothedAvgX, targetAvgX, smoothingFactor);
smoothedAvgY = p.lerp(smoothedAvgY, targetAvgY, smoothingFactor);
// --- START: Frame-Rate Independent Smoothing ---
// We use p.deltaTime to adjust the smoothing factor so that the animation
// speed remains consistent across different monitor refresh rates.
const baseSmoothing = 0.05; // Target smoothing at 60 FPS
const adjustedSmoothing = 1 - Math.pow(1 - baseSmoothing, p.deltaTime / (1000 / 60));
smoothedAvgX = p.lerp(smoothedAvgX, targetAvgX, adjustedSmoothing);
smoothedAvgY = p.lerp(smoothedAvgY, targetAvgY, adjustedSmoothing);
// --- END: Frame-Rate Independent Smoothing ---
}
} else {
smoothedAvgX = null;

1
steps/src/state.js

@ -53,6 +53,7 @@ export const appState = {
lastFrameRenderTime: 0,
lastVideoFrameTime: 0,
videoFrameRenderTime: 0,
lastOverlayUpdateTime: 0, // Track time between overlay updates for smoothing
useCustomTtcScheme: false, // Flag to switch between default and custom
customTtcScheme: {
// Default values match the UI

Loading…
Cancel
Save