diff --git a/src/Index.tsx b/src/Index.tsx
index bcde931..279721f 100644
--- a/src/Index.tsx
+++ b/src/Index.tsx
@@ -70,6 +70,15 @@ function Index() {
onValueChange={([v]) => setState({ ...state, wobbleSpeed: v })}
/>
+
+ Wobble Randomness
+ setState({ ...state, wobbleRandomness: v })}
+ />
+
);
diff --git a/src/components/canvas/MorphableShape.tsx b/src/components/canvas/MorphableShape.tsx
index e891d33..38e097d 100644
--- a/src/components/canvas/MorphableShape.tsx
+++ b/src/components/canvas/MorphableShape.tsx
@@ -46,9 +46,10 @@ export default function MorphableShape({
const flatPoints = useMemo(() => {
const wobbleAmount = state.wobble * 0.3; // scale wobble to reasonable range
- const wobbled = applyWobble(morphedPoints, time, wobbleAmount);
+ const randomness = state.wobbleRandomness / 100;
+ const wobbled = applyWobble(morphedPoints, time, wobbleAmount, randomness);
return wobbled.flatMap((p) => [p.x, p.y]);
- }, [morphedPoints, time, state.wobble]);
+ }, [morphedPoints, time, state.wobble, state.wobbleRandomness]);
return (
diff --git a/src/hooks/useShapeState.ts b/src/hooks/useShapeState.ts
index b7a9897..c918a8e 100644
--- a/src/hooks/useShapeState.ts
+++ b/src/hooks/useShapeState.ts
@@ -1,5 +1,5 @@
-import { useState, useEffect, useRef } from "react";
import type { ShapeState } from "@/types/shape";
+import { useEffect, useRef, useState } from "react";
const DEFAULT_STATE: ShapeState = {
x: 0,
@@ -9,6 +9,7 @@ const DEFAULT_STATE: ShapeState = {
size: 50, // medium
wobble: 20, // subtle
wobbleSpeed: 50, // medium
+ wobbleRandomness: 50, // medium
grain: 0, // none
color: "#FF0000", // red (C)
octave: 4, // middle octave
@@ -26,6 +27,7 @@ export function useShapeState(centerX: number, centerY: number) {
x: centerX,
y: centerY,
});
+ void initialStateRef;
// update center position when canvas resizes
useEffect(() => {
diff --git a/src/lib/shapes/wobble.ts b/src/lib/shapes/wobble.ts
index 5321d5f..2cb1616 100644
--- a/src/lib/shapes/wobble.ts
+++ b/src/lib/shapes/wobble.ts
@@ -2,14 +2,22 @@ import { noise2D } from "@/lib/noise";
import type { Point } from "./points";
// apply noise-based displacement to points. this gives a wobble effect. points are displaced radially
-export function applyWobble(points: Point[], time: number, amount: number, noiseScale = 0.5): Point[] {
+export function applyWobble(
+ points: Point[],
+ time: number,
+ amount: number,
+ randomness: number,
+ noiseScale = 0.5,
+): Point[] {
if (amount === 0) return points;
return points.map((point, i) => {
- // use point index and time for noise input
const noiseX = i * noiseScale;
const noiseY = time;
- const displacement = noise2D(noiseX, noiseY) * amount;
+ const noiseValue = noise2D(noiseX, noiseY);
+ const sineValue = Math.sin(time * 2 + i * noiseScale);
+ const blended = sineValue * (1 - randomness) + noiseValue * randomness;
+ const displacement = blended * amount;
const angle = Math.atan2(point.y, point.x);
diff --git a/src/types/shape.ts b/src/types/shape.ts
index 511da57..444aeb0 100644
--- a/src/types/shape.ts
+++ b/src/types/shape.ts
@@ -8,6 +8,7 @@ export interface ShapeState {
size: number; // 0-100, controls volume
wobble: number; // 0-100, visual wobble amount
wobbleSpeed: number; // 0-100, wobble animation speed
+ wobbleRandomness: number; // 0-100, noise vs sine blend
grain: number; // 0-100, noise mix
color: string; // hex color from clavier keyboard
octave: number; // 1-8, frequency multiplier