wobbleRandomness

interpolates between noise and sine wave
This commit is contained in:
2026-01-25 10:02:51 +00:00
parent 853e024bf7
commit 13ef090e7b
5 changed files with 27 additions and 6 deletions

View File

@@ -70,6 +70,15 @@ function Index() {
onValueChange={([v]) => setState({ ...state, wobbleSpeed: v })} onValueChange={([v]) => setState({ ...state, wobbleSpeed: v })}
/> />
</div> </div>
<div className="flex flex-col gap-2">
<span className="text-sm font-medium">Wobble Randomness</span>
<Slider
value={[state.wobbleRandomness]}
min={0}
max={100}
onValueChange={([v]) => setState({ ...state, wobbleRandomness: v })}
/>
</div>
</div> </div>
); );

View File

@@ -46,9 +46,10 @@ export default function MorphableShape({
const flatPoints = useMemo(() => { const flatPoints = useMemo(() => {
const wobbleAmount = state.wobble * 0.3; // scale wobble to reasonable range 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]); return wobbled.flatMap((p) => [p.x, p.y]);
}, [morphedPoints, time, state.wobble]); }, [morphedPoints, time, state.wobble, state.wobbleRandomness]);
return ( return (
<Group x={state.x} y={state.y} draggable onDragMove={handleDrag}> <Group x={state.x} y={state.y} draggable onDragMove={handleDrag}>

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useRef } from "react";
import type { ShapeState } from "@/types/shape"; import type { ShapeState } from "@/types/shape";
import { useEffect, useRef, useState } from "react";
const DEFAULT_STATE: ShapeState = { const DEFAULT_STATE: ShapeState = {
x: 0, x: 0,
@@ -9,6 +9,7 @@ const DEFAULT_STATE: ShapeState = {
size: 50, // medium size: 50, // medium
wobble: 20, // subtle wobble: 20, // subtle
wobbleSpeed: 50, // medium wobbleSpeed: 50, // medium
wobbleRandomness: 50, // medium
grain: 0, // none grain: 0, // none
color: "#FF0000", // red (C) color: "#FF0000", // red (C)
octave: 4, // middle octave octave: 4, // middle octave
@@ -26,6 +27,7 @@ export function useShapeState(centerX: number, centerY: number) {
x: centerX, x: centerX,
y: centerY, y: centerY,
}); });
void initialStateRef;
// update center position when canvas resizes // update center position when canvas resizes
useEffect(() => { useEffect(() => {

View File

@@ -2,14 +2,22 @@ import { noise2D } from "@/lib/noise";
import type { Point } from "./points"; import type { Point } from "./points";
// apply noise-based displacement to points. this gives a wobble effect. points are displaced radially // 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; if (amount === 0) return points;
return points.map((point, i) => { return points.map((point, i) => {
// use point index and time for noise input
const noiseX = i * noiseScale; const noiseX = i * noiseScale;
const noiseY = time; 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); const angle = Math.atan2(point.y, point.x);

View File

@@ -8,6 +8,7 @@ export interface ShapeState {
size: number; // 0-100, controls volume size: number; // 0-100, controls volume
wobble: number; // 0-100, visual wobble amount wobble: number; // 0-100, visual wobble amount
wobbleSpeed: number; // 0-100, wobble animation speed wobbleSpeed: number; // 0-100, wobble animation speed
wobbleRandomness: number; // 0-100, noise vs sine blend
grain: number; // 0-100, noise mix grain: number; // 0-100, noise mix
color: string; // hex color from clavier keyboard color: string; // hex color from clavier keyboard
octave: number; // 1-8, frequency multiplier octave: number; // 1-8, frequency multiplier