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