pacer-js 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/Pacer.js +230 -107
  2. package/README.md +6 -8
  3. package/package.json +49 -38
  4. package/pacer.svg +0 -1256
package/Pacer.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright ©️ 2025 Stewart Smith. See LICENSE for details.
1
+ // Copyright ©️ 2025–2026 Stewart Smith. See LICENSE for details.
2
2
 
3
3
 
4
4
 
@@ -27,7 +27,7 @@ import {
27
27
  normalize01,
28
28
  lerp
29
29
 
30
- } from 'shoes-js'
30
+ } from 'snacks-js'
31
31
 
32
32
 
33
33
 
@@ -53,11 +53,19 @@ class Pacer {
53
53
  constructor( label, units ){
54
54
 
55
55
  this._label = isUsefulString( label ) ? label : 'Untitled Pacer instance'
56
- this._units = isUsefulString( units ) ? units : 'ms'// ms, milliseconds, s, seconds, #, n, norm, normalize, normalized, %, percent
56
+
57
+
58
+ // This is purely to help humans reason around their Pacers.
59
+ // No particular value is required.
60
+ // All that matters is that YOU, the human, are consisent with your units.
61
+ // If you want to make use of this property, some suggested values:
62
+ // ms, milliseconds, s, seconds, #, n, norm, normalize, normalized, %, percent
63
+
64
+ this._units = isUsefulString( units ) ? units : 'ms'
57
65
 
58
66
  this.keys = []
59
67
  this.keyIndex = -1
60
- this.lastTouchedKey = null
68
+ this.lastCreatedKey = null
61
69
 
62
70
  this.values = {}
63
71
  this.n = 0
@@ -72,7 +80,16 @@ class Pacer {
72
80
 
73
81
 
74
82
 
75
- // Non-chainable.
83
+
84
+
85
+ ////////////////////////
86
+ // //
87
+ // Non-chainables //
88
+ // //
89
+ ////////////////////////
90
+
91
+
92
+ // ie. They do not return `this` (your Pacer instance).
76
93
 
77
94
  inspect( useRelative ){
78
95
 
@@ -120,7 +137,6 @@ class Pacer {
120
137
  // This logic is unnecessary,
121
138
  // but leaving it here for future reference.
122
139
 
123
-
124
140
  // const tweenLabel = keyA.tween.label
125
141
  // if( direction < 0 && tweenLabel !== 'linear' ){
126
142
 
@@ -135,9 +151,9 @@ class Pacer {
135
151
  method = this.isClamped ? normalize01 : normalize,
136
152
  n = method(
137
153
 
138
- now,
139
154
  keyA.timeAbsolute,
140
- keyB.timeAbsolute
155
+ keyB.timeAbsolute,
156
+ now
141
157
  ),
142
158
  a = Object.keys( keyA.values ),
143
159
  b = Object.keys( keyB.values ),
@@ -147,7 +163,7 @@ class Pacer {
147
163
  if( isNotUsefulNumber( a )) return output
148
164
  const b = keyB.values[ key ]
149
165
  if( isNotUsefulNumber( b )) return output
150
- output[ key ] = lerp( tween( n ), a, b )
166
+ output[ key ] = lerp( a, b, tween( n ))
151
167
  return output
152
168
 
153
169
  }, {})
@@ -162,7 +178,17 @@ class Pacer {
162
178
 
163
179
 
164
180
 
165
- // Chainable key-focussed methods.
181
+
182
+
183
+ ////////////////////
184
+ // //
185
+ // Chainables //
186
+ // //
187
+ ////////////////////
188
+
189
+
190
+ // ie. They all return `this` (your Pacer instance),
191
+ // so you can pipe from one method to another, to another…
166
192
 
167
193
  setTimeBounds(){
168
194
 
@@ -182,7 +208,7 @@ class Pacer {
182
208
 
183
209
 
184
210
  // Yes, we have to operate directly on the `keys` Array
185
- // rather than `key` element reference
211
+ // rather than a `key` element reference
186
212
  // if we want to actually write this property.
187
213
  // Otherwise it silently fails. Lovely.
188
214
  // And we don’t want to use Array.map
@@ -190,10 +216,21 @@ class Pacer {
190
216
 
191
217
  keys[ i ].index = i
192
218
 
219
+
220
+ // You know what?
221
+ // Let’s also keep track of _relative_ time
222
+ // so it’s as easy as pie
223
+ // to shift key times without more overhead.
224
+
225
+ keys[ i ].timeRelative = i === 0
226
+ ? 0
227
+ : key.timeAbsolute - keys[ i - 1 ].timeAbsolute
228
+
193
229
 
194
230
  // Also, here’s a nicety:
195
231
  // if there’s a key with no values,
196
232
  // let’s just copy the values from the previous key.
233
+ // (I have some further ideas about this… Stay tuned!)
197
234
 
198
235
  if( key.values instanceof Object !== true &&
199
236
  typeof keys[ i - 1 ] !== 'undefined' &&
@@ -201,23 +238,31 @@ class Pacer {
201
238
 
202
239
  keys[ i ].values = keys[ i - 1 ].values
203
240
 
204
- // There’s an argument to made for this instead of the above:
205
- // keys[ i ].values = Object.assign( {}, keys[ i - 1 ].values )
241
+
242
+ // There’s an argument to be made for this instead of the above:
243
+ // keys[ i ].values = Object.assign( {}, keys[ i - 1 ].values )
244
+ // Remove ability for original object owner to mutate values? Thoughts?
206
245
  }
207
246
  })
208
247
  this.setTimeBounds()
209
248
  return this
210
249
  }
250
+
251
+
252
+ // CHAINABLE “Key-based” methods,
253
+ // ie. creates or relies on `this.lastCreatedKey`.
254
+
211
255
  key( time, values, callback, isAbsolute ){
212
256
 
213
257
  if( isAbsolute !== true &&// Making a theoretical `isRelative` the default for backwards compatibility.
214
258
  this.keys.length > 0 ){
215
259
 
216
- time += this.getLastKey().timeAbsolute
260
+ // time += this.getLastKey().timeAbsolute
261
+ time += this.lastCreatedKey.timeAbsolute// IT’S TRICKY TO ROCK A RHYME !
217
262
  }
218
263
  if( this.keys.length === 0 ) this.values = values
219
264
  const key = new Key( time, values, callback )
220
- this.lastTouchedKey = key
265
+ this.lastCreatedKey = key
221
266
  this.keys.push( key )
222
267
  this.sortKeys()
223
268
  if( this.keys.length === 1 ) this.timeCursor = this.timeStart - 1
@@ -231,121 +276,185 @@ class Pacer {
231
276
 
232
277
  return this.key( timeAbsolute, values, callback, true )
233
278
  }
234
-
235
-
236
- labelPacer( s ){
237
-
238
- this._label = s
239
- return this
240
- }
241
279
  label( s ){
242
280
 
243
- this.lastTouchedKey.label = s
281
+ this.lastCreatedKey.label = s
244
282
  return this
245
283
  }
284
+ // Well this was stupid…
285
+ // Need to meditate on best approach for avoiding name collisions between
286
+ // 1. Regular old instance object properties,
287
+ // 2. Setters on this Pacer instance,
288
+ // 3. Setters actually intended for a Key instance.
289
+ // Particularly acute when it comes to .label(), for example.
290
+ // (I don’t love `label` vs `labelPacer`, but will do for now.)
246
291
  // values( v ){
247
292
 
248
- // this.lastTouchedKey.values = v
293
+ // this.lastCreatedKey.values = v
249
294
  // return this
250
295
  // }
251
- tween( fn ){
296
+ onKey( fn ){
252
297
 
253
- this.lastTouchedKey.tween = fn
298
+ this.lastCreatedKey.onKey = fn
254
299
  return this
255
300
  }
256
- clamp(){
301
+ tween( fn ){
257
302
 
258
- this.isClamped = true
303
+ this.lastCreatedKey.tween = fn
259
304
  return this
260
305
  }
261
- unclamp(){
262
-
306
+ onTween( fn ){
263
307
 
264
- this.isClamped = false
308
+ this.lastCreatedKey.onTween = fn
265
309
  return this
266
310
  }
267
- units( u ){
311
+ onCancel( fn ){
268
312
 
269
- this._units = u
313
+ this.lastCreatedKey.onCancel = fn
270
314
  return this
271
315
  }
272
316
 
273
317
 
274
- onKey( fn ){
318
+ // CHAINABLE “instance-wide” callback setters,
319
+ // ie. These do _not_ rely on `this.lastCreatedKey`
320
+ // and set callbacks for the instance to use.
321
+
322
+ onEveryKey( fn ){
275
323
 
276
- this.lastTouchedKey.onKey = fn
324
+ this._onEveryKey = fn
277
325
  return this
278
326
  }
279
- onTween( fn ){
327
+ onEveryTween( fn ){
280
328
 
281
- this.lastTouchedKey.onTween = fn
329
+ this._onEveryTween = fn
282
330
  return this
283
331
  }
284
- onCancel( fn ){
332
+ onBeforeAll( fn ){
333
+
334
+ this._onBefore = fn
335
+ return this
336
+ }
337
+ onAfterAll( fn ){
285
338
 
286
- this.lastTouchedKey.onCancel = fn
339
+ this._onAfter = fn
287
340
  return this
288
341
  }
289
342
 
290
343
 
291
- // Chainable instance-wide methods.
344
+ // CHAINABLE instance-wide commands,
345
+ // ie. These do _not_ rely on `this.lastCreatedKey`
346
+ // and issue simple property setters.
292
347
 
293
- onBeforeAll( fn ){
348
+ labelPacer( s ){// This naming gives me pause.
294
349
 
295
- this._onBefore = fn
350
+ this._label = s
296
351
  return this
297
352
  }
298
- onAfterAll( fn ){
353
+ units( u ){
299
354
 
300
- this._onAfter = fn
355
+ this._units = u
301
356
  return this
302
357
  }
303
- onEveryKey( fn ){
358
+ clamp(){
304
359
 
305
- this._onEveryKey = fn
360
+ this.isClamped = true
306
361
  return this
307
362
  }
308
- onEveryTween( fn ){
363
+ unclamp(){
309
364
 
310
- this._onEveryTween = fn
365
+ this.isClamped = false
311
366
  return this
312
367
  }
313
368
 
314
369
 
315
- // Chainable commands.
370
+ // A quick way to turn individual instances on/off,
371
+ // particuarly convenient if doing bulk updates
372
+ // like `Pacer.update()` ← Note that’s the Class method itself,
373
+ // not an instance method.
374
+
375
+ enable(){
376
+
377
+ this.isEnabled = true
378
+ return this
379
+ }
380
+ disable(){
381
+
382
+ this.isEnabled = false
383
+ return this
384
+ }
385
+
386
+
387
+ // All those moments will be lost in time,
388
+ // like tears in rain.
389
+ // Time to die.
390
+
391
+ remove(){
392
+
393
+ Pacer.remove( this )
394
+ return this
395
+ }
396
+
397
+
398
+
399
+
400
+ ////////////////
401
+ // //
402
+ // Update //
403
+ // //
404
+ ////////////////
405
+
406
+
407
+ // Yeah. This one method deserves its own rhombus comment block,
408
+ // because if it were a pickle it would wear a shirt that says
409
+ // “I’m a big dill.” Pho real.
410
+ // See also `onKey` and `onTween` below.
316
411
 
317
412
  update( now ){
318
413
 
319
414
 
320
415
  // We only need to update
321
- // if we are enabled
322
- // and we have at least one keyframe
323
- // that might have either a values object,
324
- // an onKey callback,
325
- // or would be included if there’s an onEveryKey callback.
416
+ // if our instance is enabled
417
+ // and we have at least one keyframe:
418
+ // that might have either a values{} object,
419
+ // an onKey() callback,
420
+ // or would be included if there’s an onEveryKey() callback.
326
421
 
327
422
  if( this.isEnabled !== true ) return this
328
423
  if( this.keys.length < 1 ) return this
329
424
 
330
-
331
- // So I guess we’re doing this.
425
+
332
426
  // What time is it?
333
- // And what direction are we flowing?
427
+ // Remember that “time” is just a numeric value…
428
+ // You’ll likely use it for time, sure,
429
+ // but you could also follow Scroll Pacer’s example
430
+ // and instead send pixel values
431
+ // describing an element’s relationship to the viewport.
334
432
 
335
433
  if( isNotUsefulNumber( now )) now = Date.now()
336
- if( now === this.timeCursor ) return this// Unlikely for standalone animations, but very likely for scroll animations.
337
- const direction = now < this.timeCursor ? -1 : 1
338
434
 
339
435
 
436
+ // The following is unlikely to be true for standalone animations,
437
+ // but very likely for scroll animations.
438
+
439
+ if( now === this.timeCursor ) return this
440
+
441
+
442
+ // If we made it to here, I guess we’re actually doing this.
443
+ // So what direction is our time flowing?
444
+
445
+ const direction = now < this.timeCursor ? -1 : 1
446
+
447
+
340
448
  // What’s our total N gain for the this entire instance?
341
449
 
342
450
  const method = this.isClamped ? normalize01 : normalize
343
- this.n = method( now, this.timeStart, this.timeStop )
451
+ this.n = method( this.timeStart, this.timeStop, now )
344
452
 
345
453
 
346
454
  // We know our keys are already sorted by time,
347
455
  // and we’ve previously set the convenience variables
348
456
  // `timeStart` and `timeStop`.
457
+ // (This sorting happens automagically with every key creation.)
349
458
 
350
459
  // Note 1: Direction has no effect on the order of
351
460
  // keyA and keyB, because we always want this to be true:
@@ -354,6 +463,7 @@ class Pacer {
354
463
 
355
464
  // Note 2: For this step, it is perfectly reasonable
356
465
  // for keyA or keyB to be undefined.
466
+ // That is a signal itself, not a lack of signal.
357
467
 
358
468
  let
359
469
  targetIndex = 0,
@@ -413,9 +523,10 @@ class Pacer {
413
523
  }
414
524
 
415
525
 
416
- // Let’s prep for calling onKey --
526
+ // Let’s prep for any onKey callbacks;
417
527
  // You never know what someone’s callbacks
418
- // are going to ask for on this instance!
528
+ // are going to ask for on this instance
529
+ // so it’s best to be kind and sharing.
419
530
 
420
531
  const keyIndexPrior = this.keyIndex
421
532
  this.direction = direction
@@ -423,8 +534,6 @@ class Pacer {
423
534
 
424
535
 
425
536
 
426
-
427
-
428
537
  ///////////////
429
538
  // //
430
539
  // onKey //
@@ -490,6 +599,12 @@ class Pacer {
490
599
 
491
600
  tempKey.onKey( tempKey.values, this )
492
601
  }
602
+
603
+
604
+ // In the future we may wish to distinguish
605
+ // between `onEveryKeyEarly` and `onEveryKeyLate`
606
+ // in order to allow users finer control.
607
+
493
608
  if( typeof this._onEveryKey === 'function' ){
494
609
 
495
610
  this._onEveryKey( tempKey.values, this )
@@ -503,8 +618,6 @@ class Pacer {
503
618
 
504
619
 
505
620
 
506
-
507
-
508
621
  /////////////////
509
622
  // //
510
623
  // onTween //
@@ -512,10 +625,14 @@ class Pacer {
512
625
  /////////////////
513
626
 
514
627
 
515
- // If we have just keyframed during this update() loop,
516
- // no need to attempt to tween -- in fact that could
517
- // cause an awful stutter or bounce.
518
-
628
+ // It is possible that our `now` is _exactly_ on a
629
+ // keyframe’s intended firing time.
630
+ // If that’s the case, isn’t it redundant to tween?
631
+ // YES. But the moment we tried removing the ability to
632
+ // fire tween callbacks when it aligned with a keyframe,
633
+ // we ran into unintended logical “gotchas” on the user side.
634
+ // If you are incredibly anxious about performance,
635
+ // you may want to give more thought to your callbacks here.
519
636
 
520
637
  // We need TWO valid keyframes in order to tween anything.
521
638
  // If we don’t got, we bail now.
@@ -553,19 +670,21 @@ class Pacer {
553
670
 
554
671
  keyA._onTween( this.values, this )
555
672
  }
673
+
674
+
675
+ // In the future we may wish to distinguish
676
+ // between `onEveryTweenEarly` and `onEveryTweenLate`
677
+ // in order to allow users finer control.
678
+
556
679
  if( typeof this._onEveryTween === 'function' ){
557
680
 
558
681
  this._onEveryTween( this.values, this )
559
682
  }
560
683
  }
561
-
562
-
563
684
  return this
564
685
  }
565
-
566
-
567
-
568
-
686
+
687
+
569
688
  reset( newTimeStartAbsolute ){
570
689
 
571
690
  this.disable()
@@ -576,7 +695,11 @@ class Pacer {
576
695
  .forEach( function( key, i, keys ){
577
696
 
578
697
  timeCursor += key.timeRelative
579
- keys[ i ].timeAbsolute = timeCursor// Again, see reasoning above for using .forEach rather than .reduce or .map here.
698
+
699
+
700
+ // Again, see reasoning above for using .forEach rather than .reduce or .map here.
701
+
702
+ keys[ i ].timeAbsolute = timeCursor
580
703
  })
581
704
  this.setTimeBounds()
582
705
  this.keyIndex = -1
@@ -586,37 +709,18 @@ class Pacer {
586
709
  }
587
710
 
588
711
 
589
- // A quick way to turn individual pacers on/off,
590
- // particuarly convenient if doing builk updates
591
- // like Pacer.update() ← Note that’s the Class method itself,
592
- // not an instance method.
593
-
594
- enable(){
595
-
596
- this.isEnabled = true
597
- return this
598
- }
599
- disable(){
600
-
601
- this.isEnabled = false
602
- return this
603
- }
604
-
605
712
 
606
- // All those moments will be lost in time,
607
- // like tears in rain.
608
- // Time to die.
609
713
 
610
- remove(){
611
-
612
- Pacer.remove( this )
613
- return this
614
- }
615
714
 
616
715
 
716
+ /////////////////
717
+ // //
718
+ // Statics //
719
+ // //
720
+ /////////////////
617
721
 
618
722
 
619
- // STATICS: `this === Pacer`
723
+ // ie `this === Pacer`
620
724
 
621
725
  static all = []
622
726
  static update( now ){
@@ -665,15 +769,28 @@ class Pacer {
665
769
 
666
770
 
667
771
 
772
+
773
+
774
+ ////////////////
775
+ // //
776
+ // Easing //
777
+ // //
778
+ ////////////////
779
+
780
+
668
781
  // Tweening functions, aka Easing functions.
669
782
  // “Tween” is of course short for “between”, as in _between_ the keyframes.
670
- // We’ll start with our default tween (no easing):
783
+ // We’ll start with our default tween (no fancy easing):
671
784
 
672
785
  Pacer.linear = function( n ){ return n }
673
786
  Pacer.linear.label = 'linear'
674
787
 
675
788
 
676
- // Look how purty these symetric functions are boxed up.
789
+ // Robert Penners collection of easing functions.
790
+ // https://robertpenner.com/easing/
791
+ // Look how ’purty I’ve boxed up these symetric functions!
792
+ // Compare to how much lengthier and complicated this looks:
793
+ // https://github.com/danro/jquery-easing/blob/master/jquery.easing.js
677
794
  // Down the road we ought to add Bezier() and Custom options.
678
795
 
679
796
  Object.entries({
@@ -707,6 +824,10 @@ Object.entries({
707
824
  key = entry[ 0 ],
708
825
  val = entry[ 1 ]
709
826
 
827
+
828
+ // Graft our easing logic right onto Pacer
829
+ // so it’s trivial to access.
830
+
710
831
  Pacer[ key ] = {
711
832
 
712
833
  label: key,
@@ -735,8 +856,7 @@ Object.entries({
735
856
  Pacer.bounce = {
736
857
 
737
858
  label: 'bounce',
738
- in: n => 1 - val( 1 - n ),
739
- out: function( n, n1, d1 ){
859
+ out: function( n, n1, d1 ){// Oddly, must be defined before `in` and `inOut`.
740
860
 
741
861
  if( isNotUsefulNumber( n1 )) n1 = 7.5625
742
862
  if( isNotUsefulNumber( d1 )) d1 = 2.75
@@ -745,7 +865,10 @@ Pacer.bounce = {
745
865
  else if( n < 2.5 / d1 ) return n1 * ( n -= 2.25 / d1 ) * n + 0.9375
746
866
  else return n1 * ( n -= 2.625 / d1 ) * n + 0.984375
747
867
  },
748
- inOut: n => n < 0.5 ? val( n * 2 ) / 2 : val( n * 2 - 1 ) / 2 + 0.5
868
+ in: n => 1 - Pacer.bounce.out( 1 - n ),
869
+ inOut: n => n < 0.5
870
+ ? Pacer.bounce.out( n * 2 ) / 2
871
+ : Pacer.bounce.out( n * 2 - 1 ) / 2 + 0.5
749
872
  }
750
873
  Pacer.bounce.in.label = 'bounce'
751
874
  Pacer.bounce.in.style = 'in'
package/README.md CHANGED
@@ -1,13 +1,11 @@
1
1
  <img src="./pacer.svg?raw=true" width="100%">
2
2
 
3
- <br>
4
-
5
3
 
6
4
 
7
5
 
8
6
  ## TL;DR
9
7
 
10
- __Pacer__ is a light-weight keyframing toolkit inspired by [Soledad Penadés](https://soledadpenades.com/)’ original [tween.js](https://soledadpenades.com/projects/tween-js/) masterpiece. List your keyframes as time / value pairs, and __Pacer__ will ✨ tween your numbers and 📞 call your callbacks. __It’s minimal__. Only does what it needs to. __It’s reliable__. We use this in our own professional projects. We found the bumps and sanded them down ✅ (so you won’t have to). Either include the `Pacer.js` ES6 module in your codebase, or install the [Node package](https://www.npmjs.com/package/pacer-js):
8
+ __Pacer__ is a light-weight keyframing toolkit inspired by [Soledad Penadés](https://soledadpenades.com/)’ original [tween.js](https://soledadpenades.com/projects/tween-js/) masterpiece. List your keyframes as time / value pairs, and __Pacer__ will ✨ tween your numbers and 📞 call your callbacks. __It’s minimal__. Only does what it needs to. __It’s reliable__. We use this in our own professional projects. We found the bumps and sanded them down ✅ (so you won’t have to). Either include the [`Pacer.js`](./Pacer.js) ES6 module (and its [one dependency](https://github.com/stewdio/snacks-js)) in your codebase, or install the [Node package](https://www.npmjs.com/package/pacer-js):
11
9
 
12
10
  ```shell
13
11
  npm install pacer-js
@@ -42,7 +40,7 @@ That’s it. You’re good to go 👍
42
40
 
43
41
 
44
42
 
45
- <br><br><br><br>
43
+ <br><br><br>
46
44
 
47
45
 
48
46
 
@@ -131,7 +129,7 @@ Expanding on the above, a code block should read like a normal paragraph of text
131
129
 
132
130
  ### Relative _and_ absolute timestamps
133
131
 
134
- By default, keyframes are specificed by _relative_ time. (“Do this two seconds after that last keyframe.”) This makes it trivial to swap pieces of an animation around—just cut and paste—without having to redo all the keyframe timings. Our TL;DR example uses the `key` command to illustrate this workflow, but we could have also used the slightly more descriptive `rel` (“relative”) alias to accomplish the exact same thing. All relative times are relative to the chronologically-latest keyframe as determined the moment the `key` or `rel` command is processed. (And yes, you can specify a _negative_ relative time—if you’re into that sort of thing.) What about your _first_ keyframe—which has no prior keyframe to be relative to? Consider it relative to _zero_—which makes it both relative _and_ absolute. Note the use of the alias `rel` here rather than `key`:
132
+ By default, keyframes are specificed by _relative_ time. (“Do this two seconds after that last keyframe.”) This makes it trivial to swap pieces of an animation around—just cut and paste—without having to redo all the keyframe timings. Our [TL;DR example](#tldr) uses the `key` command to illustrate this workflow, but we could have also used the slightly more descriptive `rel` (“relative”) alias to accomplish the exact same thing. All relative times are relative to the _most recently created keyframe_ as determined the moment the `key` or `rel` command is processed. (And yes, you can specify a _negative_ relative time—if you’re into that sort of thing.) What about your _first_ keyframe—which has no prior keyframe to be relative to? Consider it relative to _zero_—which makes it both relative _and_ absolute. Note the use of the alias `rel` here rather than `key`:
135
133
 
136
134
  ```javascript
137
135
  var now = Date.now()
@@ -174,7 +172,7 @@ new Pacer()
174
172
  .onKey(()=> console.log( '2nd keyframe' ))
175
173
  ```
176
174
 
177
- When you create a keyframe, it is added to your instance’s `keys` array, and that array of keyframes is then sorted in chronological order according to each keyframe’s absolute time. Meanwhile, your instance also keeps track of the last keyframe you have “touched” via a `lastTouchedKey` property, so that subsequent commands like `onKey` or `tween` always refer to that last mentioned keyframe.
175
+ When you create a keyframe, it is added to your instance’s `keys` array, and that array of keyframes is then sorted in chronological order according to each keyframe’s absolute time. (This ensures your `keys` array is always tidy.) Meanwhile, your instance also keeps track of the last keyframe you have created via a `lastCreatedKey` property, so that subsequent commands like `onKey` or `tween` always refer to the “intuitively correct” keyframe. Your code reads like a short story.
178
176
 
179
177
 
180
178
 
@@ -240,7 +238,7 @@ Each easing equation includes its `in`, `out`, and `inOut` variants, eg. `Pacer.
240
238
 
241
239
  ### Every key, every tween
242
240
 
243
- If you find you’re running the same callback over and over, perhaps you’d prefer to declare that just once? We’ve got you covered. Use `onEveryKey` to declare a callback that will fire on _every_ keyframe, and `onEveryTween` to do the same for all tweens. [Something borrowed, something blue. Every tween callback for you](https://youtu.be/4YR_Mft7yIM).
241
+ If you find you’re running the same callback over and over, perhaps you’d prefer to declare that just once? We’ve got you covered. Use `onEveryKey` to declare a callback that will fire on _every_ keyframe, and `onEveryTween` to do the same for all tweens. [Something borrowed, something blue. Every key and tween for you](https://youtu.be/4YR_Mft7yIM).
244
242
 
245
243
 
246
244
  ```javascript
@@ -462,7 +460,7 @@ Another thing to note is that `update` expects an _absolute_ number, rather than
462
460
 
463
461
  ### Forward _and_ backward
464
462
 
465
- Mathematically, [time can flow both forward _and_ backward](https://en.wikipedia.org/wiki/Tenet_(film)). Why would __Pacer__ ignore that reality? The ability to scrub a timeline back and forth is incredibly valuable, and literally the mechanism that our __ScrollPacer__ toolkit uses for scroll-based animations. (More on this to come.) Rest assured that your `update` call can handle time flowing in either direction (and at any speed). It just works.
463
+ Mathematically, [time can flow both forward _and_ backward](https://en.wikipedia.org/wiki/Tenet_(film)). Why would __Pacer__ ignore that reality? The ability to scrub a timeline back and forth is incredibly valuable, and literally the mechanism that our [__Scroll Pacer__](https://github.com/stewdio/scroll-pacer-js) toolkit uses for scroll-based animations. (More on this to come.) Rest assured that your `update` call can handle time flowing in either direction (and at any speed). It just works.
466
464
 
467
465
 
468
466