How to Troubleshoot Filmstrip Cache Misses and Memory Pressure Issues in Clypra
Clypra employs a multi-layered caching system combining a viewport-bounded FilmstripCache with a tile-addressable FilmstripTileCache to render video thumbnails efficiently; diagnosing issues requires monitoring memory budgets, velocity states, and LRU eviction cycles through the exposed debugging APIs.
Clypra's filmstrip rendering pipeline relies on a sophisticated interplay between a filmstrip cache, a tile-addressable tile cache, and a viewport-bounded request scheduler to deliver smooth thumbnail scrolling. When thumbnails flicker, disappear, or consume excessive memory, developers must understand how these layers interact to troubleshoot filmstrip cache misses and memory pressure issues in Clypra effectively.
Understanding the Filmstrip Cache Architecture
Core Components
The rendering stack spans several modules:
FilmstripCache(src/lib/renderEngine/FilmstripCache.ts): Manages viewport-bounded thumbnail requests, groups updates per animation frame, and owns allImageBitmapinstances.FilmstripTileCache(src/lib/filmstrip/FilmstripTileCache.ts): Stores individual thumbnails keyed by tile addresses for reuse across zoom levels and rapid scrolling.filmstripTiers.ts(src/lib/filmstrip/filmstripTiers.ts): Generates deterministic grid addresses for viewport and zoom tier calculations.useFilmstrip.ts(src/lib/filmstrip/useFilmstrip.ts): React hook subscribing to cache updates and triggering UI re-renders.
Memory Management Strategy
Clypra implements strict memory controls:
- Hard budget: Default 100 MiB configured via
new FilmstripCache(100). - LRU eviction: When memory exceeds the budget, the oldest clip entry (by
lastViewportUpdate) is purged via_evictLRU. - Tile cache isolation:
FilmstripTileCachemaintains a separate budget contributing to overall utilization reported bygetStats.
Cache-Miss Handling Mechanisms
The system optimizes for perceived performance:
- Viewport-bounded generation: Only tiles intersecting the visible area (plus overscan) are requested via
generateViewportFilmstripTimestampsandgenerateViewportTileAddresses. - Aggressive cheating: During fast scrolling (
velocityState >= VelocityState.Fast), the cache falls back to the nearest cached tile through_buildArtifactsFromTilesto prevent blank UI states. - Debounced requests: Duplicate tile addresses requested within ~100 ms reuse cached entries without triggering new transport requests.
Common Symptoms and Diagnostic Patterns
| Symptom | Likely Root Cause |
|---|---|
| Blank or flickering filmstrip during scrolling | Tile cache miss under high velocity, or incorrect velocityState triggering frequent re-requests. |
| Out-of-memory warnings | Memory budget too low, excessive clips retained, or FilmstripTileCache failing to evict via tileCache.dispose. |
| UI stutter or rerender storms | Missing RAF batching in scheduleArtifactUpdate or excessive pending artifacts. |
| Stale thumbnails after clip removal | Missing invalidateClip call when the store deletes a clip without notifying the cache. |
| Artifacts never appearing | Silent failure in requestProgressiveTiers due to epochId mismatches. |
Step-by-Step Troubleshooting Guide
-
Verify Cache Budget Utilization
Check if the cache is nearing its limit:
const stats = renderEngine.filmstripCache.getStats(); console.log('Filmstrip cache stats', stats);If
memoryMBapproachesbudgetMB(above 90%) with highutilizationPercent, increase the budget in theRenderEngineconstructor:new RenderEngine({ filmstripMemoryMB: 200 }). -
Validate Velocity State Propagation
Ensure
RenderEngineupdatesfilmstripCache.setVelocityStateon every scroll or zoom event. A stale state prevents the "aggressive cheating" fallback during fast scrolling. Verify this insrc/lib/renderEngine/RenderEngine.tsaround line 80. -
Detect Cache Misses
Enable debug logging in
FilmstripCache._buildArtifactsFromTiles:console.debug('[FilmstripCache] cache miss for tile', addr);Alternatively, monitor the temporary logger output while reproducing the issue to identify specific tile addresses failing to resolve.
-
Inspect Tile Address Generation
Validate that
generateViewportTileAddressesreturns non-empty arrays for visible clips. If it returns[](lines 92-94 in the source), the viewport parameters (viewportScrollLeft,viewportWidth,pixelsPerSecond) may diverge from the actual UI layout. -
Force LRU Eviction Testing
Manually trigger eviction to verify stale tile cleanup:
renderEngine.filmstripCache._evictLRU(); // private method for debugging onlyObserve whether fresh tiles are requested and displayed correctly after eviction.
-
Confirm Clip Invalidation
When clips are removed or epochs change, verify that
invalidateClipis called. Search forinvalidateClip(intimelineStoreorrecordingStoreto ensure proper lifecycle integration. -
Monitor RAF Batching
Excessive
scheduleArtifactUpdatecalls withoutflushPendingArtifactsindicate a lostrafId. EnsurerequestAnimationFrameis not being throttled by polyfills that limit frame rates. -
Analyze Tile Cache Statistics
Inspect tile-level metrics:
const tileStats = renderEngine.filmstripCache.tileCache.getStats(); console.log('Tile cache', tileStats);Low
tileUtilizationPercentsuggests premature eviction causing frequent cache misses. -
Isolate with Minimal Reproduction
Create a timeline with a single short clip, set the viewport to cover it fully, and observe the filmstrip. Gradually add clips or increase zoom to identify the threshold where issues manifest.
-
Run Unit Test Coverage
Execute the repository's test suite to surface regressions:
npm run test -- src/lib/__tests__/useFilmstrip.test.ts npm run test -- src/lib/filmstrip/__tests__/FilmstripTileCache.test.ts
Practical Debugging Code Examples
Adjusting the Memory Budget
Increase the filmstrip cache capacity when deploying to high-memory devices:
import { RenderEngine } from './core/renderEngine/renderEngine';
const engine = new RenderEngine({
filmstripMemoryMB: 200, // default is 100 MiB
});
Inspecting Runtime Statistics
Monitor cache health in your debug UI:
const stats = engine.filmstripCache.getStats();
console.table({
ClipCount: stats.clipCount,
MemoryUsed: `${stats.memoryMB} MiB`,
Budget: `${stats.budgetMB} MiB`,
Utilisation: `${stats.utilizationPercent}%`,
TileCount: stats.tileCount,
});
Logging Cache Misses
Patch the prototype for development debugging:
const originalBuild = FilmstripCache.prototype._buildArtifactsFromTiles;
FilmstripCache.prototype._buildArtifactsFromTiles = function (addresses, clipId, tier, videoPath) {
const artifacts = originalBuild.call(this, addresses, clipId, tier, videoPath);
if (artifacts.length < addresses.length) {
console.warn('[Debug] Filmstrip cache miss:', {
clipId,
missing: addresses.length - artifacts.length,
});
}
return artifacts;
};
Forcing Complete Cache Clearance
Dispose the entire cache when suspecting memory leaks:
engine.filmstripCache.dispose();
// Re-initialize if continued rendering is required
engine.filmstripCache = new FilmstripCache(200);
Summary
- Clypra's filmstrip rendering relies on
FilmstripCacheandFilmstripTileCacheworking in tandem with a viewport-bounded request scheduler. - Memory pressure arises when the 100 MiB default budget is exceeded or when LRU eviction fails to trigger via
_evictLRU. - Cache misses typically indicate incorrect
velocityStatepropagation, mismatched viewport parameters, or missing tile addresses ingenerateViewportTileAddresses. - Use
getStats()to monitor utilization andinvalidateClip()to prevent stale data when clips are removed. - The React hook
useFilmstrip.tsprovides the UI subscription layer, whilerenderEngine.tsmanages the velocity state updates critical for fallback rendering during fast scrolling.
Frequently Asked Questions
Why do thumbnails disappear when I scroll quickly?
Rapid scrolling triggers velocityState checks in FilmstripCache.ts. If the state is not set to VelocityState.Fast or the fallback mechanism _buildArtifactsFromTiles cannot locate nearest cached tiles, the UI renders blank spaces. Verify that your scroll handler calls setVelocityState with the correct enum value from RenderEngine.ts.
How do I know if memory pressure is causing performance issues?
Call renderEngine.filmstripCache.getStats() and check if memoryMB exceeds 90% of budgetMB. High utilization forces aggressive LRU eviction in _evictLRU, which can cause stuttering as the system purges and rebuilds the cache. Consider increasing filmstripMemoryMB in the constructor or investigating tileCache.dispose calls that might not be releasing memory.
What causes the "rerender storm" in the filmstrip component?
This occurs when scheduleArtifactUpdate is invoked repeatedly without proper RAF batching. Ensure that flushPendingArtifacts is being called within the animation frame loop. Check that requestAnimationFrame is not being intercepted by polyfills that limit the frame rate, as this can cause the rafId tracking to fail.
Why do deleted clips still show thumbnails in the filmstrip?
The cache retains tiles until invalidateClip is called with the specific clip ID. If your timelineStore or recordingStore removes clips without notifying the cache, zombie thumbnails persist. Audit your data layer to ensure invalidateClip(clipId) is invoked whenever clip epochs change or deletions occur.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →