FUTURE FUN: THREE FEATURES TO LAND ================================== End-of-day backlog of ideas Claude pitched and the user liked. Each sized for ~one patch bump. Wiggle goes first; the others can wait. 1. WIGGLE — procedural shake modifier (per layer) ------------------------------------------------- Add a "Wiggle" property to Layer. When on, every sampled frame adds deterministic noise to position and/or rotation, giving any layer that classic animator wobble. Pairs thematically with Kaleido: both are per-layer procedural modifiers stacked on top of the base keyframe sample. Data model (src/types/scene.ts): wiggle?: { amplitude?: number // pixels for x/y rotation?: number // degrees for rotation frequency?: number // Hz (cycles per second) } // Defaults when `wiggle` is set but a sub-field is missing: // amplitude=8px, rotation=2°, frequency=2Hz. Toggle off ⇒ delete. Math (src/lib/wiggle.ts) — shared truth: function layerSeed(id: string): [number, number, number] // 3 stable 32-bit ints derived from the layer id, used as // independent seeds for x / y / rotation noise streams. function wiggleNoise(seed: number, t: number): number // Deterministic [-1, 1] output. Sum of three sin waves with // different irrational phase offsets — cheap, smooth, no // dependency on a noise lib. return ( sin(seed * 12.9898 + t * 78.233) + sin(seed * 4.31 + t * 11.71 ) + sin(seed * 7.71 + t * 23.5 ) ) / 3 function applyWiggle(props, layer, time): props' // Adds amp * noise(seedX, t * freq) to props.x, etc. Wire-in: - src/lib/interpolate.ts sampleLayer: post-process the result of the keyframe sample with applyWiggle. ALL renderers go through sampleLayer (canvas2d, Konva, button-state, clip-internal), so a single hook here lights up everywhere except the export engine. - src/lib/exportPlayer.ts: mirror the math in JS inside the engine source so exports look identical. RUNTIME_VERSION bump. UI (src/components/PropertiesPanel.vue + i18n): - New "Wiggle" collapsible section, between Mask and Kaleido feels natural. Toggle (PhWaveSine icon), then three RangeSliders: amplitude (0–64 px), rotation (0–45°), frequency (0–10 Hz). Hidden for button-asset layers (consistent with Kaleido). Store (src/stores/scene.ts): - setLayerWiggle(id, patch) with same delete-on-default semantics as Kaleido (rotation=0 ⇒ delete the field, etc.). Tests: - wiggleNoise determinism: noise(seed, t) === noise(seed, t). - wiggleNoise smoothness: |n(t) - n(t+1/60)| stays bounded. - sampleLayer with wiggle: x/y/rotation drift around base, return to base when wiggle is off. - Engine source carries wiggleNoise; same sample at the same time matches between editor and engine. - PropertiesPanel renders the new section + writes wiggle fields back to the store. 2. MOTION-PATH OVERLAY (selected layer's position keyframes) ------------------------------------------------------------ Visualize a layer's keyframed path on the canvas as a faint dashed line connecting consecutive position keyframes, with each keyframe rendered as a draggable dot. The position-keyframe drag-handle infrastructure (PathFrame, activeSegments in CanvasStage.vue ~2562) is already 70% there — currently only used when a position-kf is explicitly being dragged. Promote that overlay to "always on while the layer is selected". Scope: - Toggle in the View menu (or a slash-key shortcut) called "Motion paths" — global flag persisted in localStorage like `useShowLocked`. - For the selected layer (and only the selected layer), draw the overlay even when nothing is being dragged. Reuse the existing segment / curve-sampling helpers. - Color follows layer.color so two layers' paths don't collide visually. Bigger lift than Wiggle but mostly leveraging existing code. 3. EYEDROPPER (color sampling from anywhere on the page) -------------------------------------------------------- Tiny feature using the browser's EyeDropper API. Adds a small pipette icon next to every color swatch in the PropertiesPanel (fill, stroke, shadow color). Click → fires `new EyeDropper().open()` → user picks any pixel on screen → result sets the swatch. Caveats: - Chromium-only API. Hide the button when `window.EyeDropper` is undefined (Safari + Firefox) — a clean progressive enhancement. - Permission prompt is browser-handled, no extra plumbing. Smallest of the three. Good "polish day" snack.