jevstrudel: a Drawer's frame never queries backwards. After a restart the scheduler counts from 0 again, and a lastFrame left from the song before made the first frame query the new pattern from that song's end: the new song's jev()s took that cycle as the playhead, and every section counted as begun before Jev was asked (2026-09-27).
9describe('Drawer', () => { 10 it('queries nothing before where it restarted', () => { 11 let now = 0; 12 const arcs = []; 13 const pattern = { queryArc: (b, e) => (arcs.push([b, e]), []) }; 14 const scheduler = { now: () => now, pattern }; 15 globalThis.requestAnimationFrame = vi.fn(() => 1); 16 globalThis.cancelAnimationFrame = vi.fn(); 17 const drawer = new Drawer(() => {}, [0, 0]); 18 drawer.start(scheduler); 19 const frame = () => drawer.framer.onFrame(); 20 frame(); 21 now = 93; 22 frame(); 23 // the old song ends, the next starts from 0 24 drawer.stop(); 25 now = 0; 26 arcs.length = 0; 27 drawer.start(scheduler); 28 frame(); 29 now = 0.05; 30 frame(); 31 expect(arcs.every(([b]) => b < 1)).toBe(true); 32 }); 33 34 it('skips a frame whose time went backwards', () => { 35 let now = 50; 36 const arcs = []; 37 const scheduler = { now: () => now, pattern: { queryArc: (b, e) => (arcs.push([b, e]), []) } }; 38 const drawer = new Drawer(() => {}, [0, 0]); 39 drawer.scheduler = scheduler; 40 drawer.framer.onFrame(); 41 now = 0; 42 drawer.framer.onFrame(); 43 now = 0.05; 44 drawer.framer.onFrame(); 45 expect(arcs).toEqual([[0, 0.05]]); 46 }); 47});