44 *
55 * Supports two timing modes:
66 * 1. frameRatesForFrames (FPS) - Frame timing in frames-per-second (default)
7- * 2. beatsPerFrame (BPM sync) - Frame timing in beats, synced to current BPM
7+ * 2. frameDurationBeats (BPM sync) - Frame timing in beats, synced to current BPM
88 * When MIDI clock is active, uses real-time clock pulses (24 PPQN)
99 * When no clock, falls back to time-based BPM calculation
1010 */
@@ -18,7 +18,7 @@ class AnimationLayer {
1818 #numberOfFrames;
1919 #framesPerRow;
2020 #frameRatesForFrames;
21- #beatsPerFrame ; // Array or single number for BPM sync
21+ #frameDurationBeats ; // Array or single number for BPM sync
2222 #frameWidth;
2323 #frameHeight;
2424 #loop;
@@ -35,11 +35,11 @@ class AnimationLayer {
3535 #isFinished = false ;
3636 #defaultFrameRate; // Cached fallback rate when frame-specific rate is undefined
3737 #useBPMSync = false ; // Whether to use BPM sync mode
38- #pulsesPerFrame; // Array of pulses per frame for clock sync (derived from beatsPerFrame )
38+ #pulsesPerFrame; // Array of pulses per frame for clock sync (derived from frameDurationBeats )
3939 #pulseCount = 0 ; // Accumulated clock pulses since last frame advance
4040 #unsubscribeClock = null ; // Cleanup for clock subscription
4141
42- constructor ( { canvas2dContext, image, numberOfFrames, framesPerRow, loop = true , frameRatesForFrames = { 0 : 1 } , beatsPerFrame = null , retrigger = true , bitDepth = null } ) {
42+ constructor ( { canvas2dContext, image, numberOfFrames, framesPerRow, loop = true , frameRatesForFrames = { 0 : 1 } , frameDurationBeats = null , retrigger = true , bitDepth = null } ) {
4343 if ( ! numberOfFrames || numberOfFrames < 1 ) {
4444 throw new Error ( 'AnimationLayer requires numberOfFrames >= 1' ) ;
4545 }
@@ -53,26 +53,26 @@ class AnimationLayer {
5353 this . #framesPerRow = framesPerRow ;
5454 this . #bitDepth = bitDepth ;
5555
56- // Process beatsPerFrame - supports both clock sync and time-based BPM sync
56+ // Process frameDurationBeats - supports both clock sync and time-based BPM sync
5757 // When MIDI clock is active, uses clock pulses for real-time sync (24 PPQN)
5858 // When no clock, falls back to time-based BPM calculation
59- if ( beatsPerFrame !== null && beatsPerFrame !== undefined ) {
59+ if ( frameDurationBeats !== null && frameDurationBeats !== undefined ) {
6060 this . #useBPMSync = true ;
6161
62- if ( Array . isArray ( beatsPerFrame ) ) {
62+ if ( Array . isArray ( frameDurationBeats ) ) {
6363 // Enforce strict array length equal to numberOfFrames
64- if ( beatsPerFrame . length !== numberOfFrames ) {
65- throw new Error ( `AnimationLayer: beatsPerFrame array length (${ beatsPerFrame . length } ) must equal numberOfFrames (${ numberOfFrames } )` ) ;
64+ if ( frameDurationBeats . length !== numberOfFrames ) {
65+ throw new Error ( `AnimationLayer: frameDurationBeats array length (${ frameDurationBeats . length } ) must equal numberOfFrames (${ numberOfFrames } )` ) ;
6666 }
67- this . #beatsPerFrame = beatsPerFrame ;
67+ this . #frameDurationBeats = frameDurationBeats ;
6868 // Pre-calculate pulsesPerFrame for when clock is active (24 PPQN)
69- this . #pulsesPerFrame = beatsPerFrame . map ( b => Math . round ( b * 24 ) ) ;
70- } else if ( typeof beatsPerFrame === 'number' && beatsPerFrame > 0 ) {
69+ this . #pulsesPerFrame = frameDurationBeats . map ( b => Math . round ( b * 24 ) ) ;
70+ } else if ( typeof frameDurationBeats === 'number' && frameDurationBeats > 0 ) {
7171 // Shorthand: single number applies to all frames
72- this . #beatsPerFrame = Array ( numberOfFrames ) . fill ( beatsPerFrame ) ;
73- this . #pulsesPerFrame = Array ( numberOfFrames ) . fill ( Math . round ( beatsPerFrame * 24 ) ) ;
72+ this . #frameDurationBeats = Array ( numberOfFrames ) . fill ( frameDurationBeats ) ;
73+ this . #pulsesPerFrame = Array ( numberOfFrames ) . fill ( Math . round ( frameDurationBeats * 24 ) ) ;
7474 } else {
75- throw new Error ( 'AnimationLayer: invalid beatsPerFrame ' ) ;
75+ throw new Error ( 'AnimationLayer: invalid frameDurationBeats ' ) ;
7676 }
7777
7878 // Subscribe to MIDI clock events for real-time sync when clock is active
@@ -149,12 +149,12 @@ class AnimationLayer {
149149
150150 /**
151151 * Advance the animation frame based on elapsed time
152- * Uses BPM sync if beatsPerFrame is defined, otherwise uses frameRatesForFrames
152+ * Uses BPM sync if frameDurationBeats is defined, otherwise uses frameRatesForFrames
153153 * Clock sync mode skips time-based advancement (pulses drive frames directly)
154154 * @param {number } timestamp - Current timestamp
155155 */
156156 #advanceFrame( timestamp ) {
157- // When clock is active and we have beatsPerFrame , let pulses drive frames
157+ // When clock is active and we have frameDurationBeats , let pulses drive frames
158158 if ( this . #useBPMSync && this . #pulsesPerFrame && appState . bpmSource === 'clock' ) {
159159 return ;
160160 }
@@ -213,16 +213,16 @@ class AnimationLayer {
213213
214214 /**
215215 * Calculate the interval (ms) for a given frame
216- * Uses BPM sync if beatsPerFrame is defined, otherwise uses frameRatesForFrames
216+ * Uses BPM sync if frameDurationBeats is defined, otherwise uses frameRatesForFrames
217217 * @param {number } frameIndex - The frame index
218218 * @returns {number } - Interval in milliseconds
219219 */
220220 #getFrameInterval( frameIndex ) {
221- if ( this . #useBPMSync && this . #beatsPerFrame ) {
222- // BPM sync mode: interval = (beatsPerFrame * 60000) / bpm
223- // beatsPerFrame [i] = number of beats this frame should last
224- // e.g., beatsPerFrame =0.25 at 120 BPM = 125ms (16th note)
225- const beats = this . #beatsPerFrame [ frameIndex ] ?? this . #beatsPerFrame [ 0 ] ?? 0.25 ;
221+ if ( this . #useBPMSync && this . #frameDurationBeats ) {
222+ // BPM sync mode: interval = (frameDurationBeats * 60000) / bpm
223+ // frameDurationBeats [i] = number of beats this frame should last
224+ // e.g., frameDurationBeats =0.25 at 120 BPM = 125ms (16th note)
225+ const beats = this . #frameDurationBeats [ frameIndex ] ?? this . #frameDurationBeats [ 0 ] ?? 0.25 ;
226226 // Ensure BPM is at least the configured minimum to prevent extremely long intervals.
227227 // Fallback to 1 if settings.bpm.min is 0 or invalid to prevent division by zero.
228228 const minBPM = settings . bpm . min > 0 ? settings . bpm . min : 1 ;
0 commit comments