The performance panel provides quality presets and granular controls to tune rendering intensity for your device. All settings persist across toys and are stored locally.
The performance panel appears in the top-right corner when a toy loads. It exposes three primary controls:
Pixel ratio cap
Limits resolution on high-DPI displays to reduce GPU load: // From performance-panel.ts:22-24
const MIN_PIXEL_RATIO = 1 ;
const MAX_PIXEL_RATIO = 3 ;
Range : 1.00x to 3.00x
Default : 2.00x
Effect : Lower values render at reduced resolution, improving frame rate on retina/high-DPI screens
Particle budget
Scales particle counts for all toys: // From performance-panel.ts:24-25
const MIN_PARTICLE_BUDGET = 0.4 ;
const MAX_PARTICLE_BUDGET = 1.6 ;
Range : 40% to 160%
Default : 100%
Effect : 1.0 keeps default particle counts; lower values reduce density and motion intensity
Shader quality
Chooses shader complexity:
Low (faster) : Simplified shaders for older GPUs
Balanced : Default quality for most devices
High (detailed) : Full shader features for high-end GPUs
// From performance-panel.ts:1
export type ShaderQuality = 'low' | 'balanced' | 'high' ;
Quality presets
The settings panel includes 5 quality presets that bundle pixel ratio, render scale, and particle scale:
Battery saver
// From settings-panel.ts:13-19
{
id : 'performance' ,
label : 'Battery saver' ,
description : 'Lower pixel ratio and fewer particles for older GPUs.' ,
maxPixelRatio : 1.25 ,
renderScale : 0.9 ,
particleScale : 0.65 ,
}
Use case : Older GPUs, mobile devices, or thermal constraints
Low motion
// From settings-panel.ts:22-28
{
id : 'low-motion' ,
label : 'Low motion' ,
description : 'Reduce particle density and shimmer for calmer motion.' ,
maxPixelRatio : 1.5 ,
renderScale : 0.95 ,
particleScale : 0.5 ,
}
Use case : Sensory comfort, reduced-motion preference
TV balanced
// From settings-panel.ts:30-36
{
id : 'tv' ,
label : 'TV balanced' ,
description : 'Comfortable 10-foot visuals with lower DPI for steady frame pacing.' ,
maxPixelRatio : 1.25 ,
renderScale : 0.9 ,
particleScale : 0.75 ,
}
Use case : Large displays, 10-foot viewing distance
Balanced (default)
// From settings-panel.ts:38-44
{
id : 'balanced' ,
label : 'Balanced (default)' ,
description : 'Native look with capped DPI for most laptops and desktops.' ,
maxPixelRatio : 2 ,
renderScale : 1 ,
particleScale : 1 ,
}
Use case : Most laptops and desktops
Hi-fi visuals
// From settings-panel.ts:46-53
{
id : 'hi-fi' ,
label : 'Hi-fi visuals' ,
description : 'Higher fidelity for beefy GPUs. May increase thermal load.' ,
maxPixelRatio : 2.5 ,
renderScale : 1 ,
particleScale : 1.35 ,
}
Use case : High-end GPUs, desktop gaming rigs
Hi-fi visuals may increase GPU temperature and fan noise. Monitor your device’s thermal state.
Persistent settings
All performance settings are stored in localStorage and apply across toys:
// From performance-panel.ts:21
const STORAGE_KEY = 'stims:performance-settings' ;
Settings include:
// From performance-panel.ts:3-7
export type PerformanceSettings = {
maxPixelRatio : number ;
particleBudget : number ;
shaderQuality : ShaderQuality ;
};
URL overrides
You can override stored settings via URL parameters:
https://no.toil.fyi/toy.html?toy=holy&maxPixelRatio=1.5&particleBudget=0.8&shaderQuality=low
URL parameters take precedence over stored values:
// From performance-panel.ts:52-86
function parseUrlSettings () : Partial < PerformanceSettings > {
const params = new URLSearchParams ( window . location . search );
const urlMaxPixelRatio = params . get ( 'maxPixelRatio' );
const urlParticleBudget = params . get ( 'particleBudget' );
const urlShaderQuality = params . get ( 'shaderQuality' );
// ...
}
Toys can access and subscribe to performance settings programmatically:
Get active settings
import { getActivePerformanceSettings } from './core/performance-panel.ts' ;
const settings = getActivePerformanceSettings ();
console . log ( settings . maxPixelRatio ); // 2
console . log ( settings . particleBudget ); // 1
console . log ( settings . shaderQuality ); // 'balanced'
Subscribe to changes
import { subscribeToPerformanceSettings } from './core/performance-panel.ts' ;
const unsubscribe = subscribeToPerformanceSettings (( settings ) => {
console . log ( 'Settings updated:' , settings );
// Update toy rendering parameters
});
// Later: unsubscribe()
Update settings programmatically
import { setPerformanceSettings } from './core/performance-panel.ts' ;
setPerformanceSettings ({
maxPixelRatio: 1.5 ,
particleBudget: 0.8 ,
});
Quality preset details
Each preset applies three scaling factors:
Max pixel ratio
Caps window.devicePixelRatio for the renderer:
// Example: capping at 2x on a 3x retina display
const effectivePixelRatio = Math . min ( window . devicePixelRatio , maxPixelRatio );
renderer . setPixelRatio ( effectivePixelRatio );
Impact : Lower values reduce canvas resolution, improving fill-rate-bound performance.
Render scale
Scales the canvas size relative to the window:
const canvasWidth = window . innerWidth * renderScale ;
const canvasHeight = window . innerHeight * renderScale ;
renderer . setSize ( canvasWidth , canvasHeight );
Impact : Lower values render fewer pixels, improving both fill rate and shader performance.
Particle scale
Scales the default particle count for all particle systems:
const particleCount = Math . floor ( baseCount * particleScale );
Impact : Lower values reduce draw calls and vertex processing.
From the README:
The performance panel includes 5 presets:
Battery saver : Older GPUs, mobile devices
Low motion : Reduced particle density for sensory comfort
TV balanced : 10-foot viewing
Balanced : Default for most devices
Hi-fi visuals : High-end GPUs
See the Getting Started guide for preset recommendations.
Lower pixel ratio on high-DPI displays
Retina and 4K displays render at 2x–3x the canvas resolution. Lower the pixel ratio cap to 1.25x–1.5x for a 30–50% fill-rate reduction with minimal visual loss.
Particle-heavy toys (e.g., Bubble Harmonics, Fractal Kite Garden) benefit from a 0.6x–0.8x particle budget on mid-tier GPUs.
Switch to Low shader quality
The Low (faster) shader quality disables expensive effects:
Bloom and glow passes
Complex normal mapping
Multi-pass reflections
The system does not include a built-in FPS counter, but you can monitor performance via browser DevTools:
Open DevTools Performance Monitor
Chrome/Edge : Ctrl+Shift+P (or Cmd+Shift+P on Mac) → Show Performance Monitor
Firefox : Ctrl+Shift+E (or Cmd+Opt+E on Mac) → Performance tab
Watch key metrics
FPS : Target 60 FPS (or 120 FPS on high-refresh displays)
GPU usage : Should stay below 90% for sustained periods
Frame time : Target <16.7ms (60 FPS) or <8.3ms (120 FPS)
Adjust settings if needed
If FPS drops below 45 or GPU usage exceeds 95%:
Lower Pixel ratio cap to 1.5x or 1.25x
Reduce Particle budget to 70% or 50%
Switch Shader quality to Low (faster)
Next steps
Accessibility Configure motion comfort and reduced-motion modes
Playing toys Browse and launch toys from the library