refactor help
This commit is contained in:
@ -576,17 +576,21 @@ export class FakeShader {
|
|||||||
'x^y',
|
'x^y',
|
||||||
'x&y',
|
'x&y',
|
||||||
'x|y',
|
'x|y',
|
||||||
'(x*y)%256',
|
'a|d|r',
|
||||||
|
'x|n*t^b*(t % 1.0)',
|
||||||
'(x+y+t*10)%256',
|
'(x+y+t*10)%256',
|
||||||
'((x>>4)^(y>>4))<<4',
|
'((x>>4)^(y>>4))<<4',
|
||||||
'(x^y^(x*y))%256',
|
'(x^y^(x*y))%256',
|
||||||
|
'd * t / 2.0',
|
||||||
'((x&y)|(x^y))%256',
|
'((x&y)|(x^y))%256',
|
||||||
'(x+y)&255',
|
'(x+y)&255',
|
||||||
|
'a^d * [b, r**t][floor(t%2.0)]',
|
||||||
'x%y',
|
'x%y',
|
||||||
'(x^(y<<2))%256',
|
|
||||||
'((x*t)^y)%256',
|
'((x*t)^y)%256',
|
||||||
'(x&(y|t*8))%256',
|
'(x&(y|t*8))%256',
|
||||||
'((x>>2)|(y<<2))%256',
|
'a+d*t',
|
||||||
|
'n*t*400',
|
||||||
|
'((x>>2)|(y<<2))%88',
|
||||||
'(x*y*t)%256',
|
'(x*y*t)%256',
|
||||||
'(x+y*t)%256',
|
'(x+y*t)%256',
|
||||||
'(x^y^(t*16))%256',
|
'(x^y^(t*16))%256',
|
||||||
@ -599,11 +603,14 @@ export class FakeShader {
|
|||||||
'((x|t)^(y|t))%256',
|
'((x|t)^(y|t))%256',
|
||||||
];
|
];
|
||||||
|
|
||||||
const vars = ['x', 'y', 't', 'i'];
|
const vars = ['x', 'y', 't', 'i', 'a', 'd', 'n', 'r', 'u', 'v', 'd', 'b'];
|
||||||
const ops = ['^', '&', '|', '+', '-', '*', '%'];
|
const ops = ['^', '&', '|', '+', '-', '*', '%', '**', '%'];
|
||||||
const shifts = ['<<', '>>'];
|
const shifts = ['<<', '>>'];
|
||||||
const numbers = ['2', '4', '8', '16', '32', '64', '128', '256'];
|
|
||||||
|
|
||||||
|
const numbers = [];
|
||||||
|
for (let i = 0; i < Math.random(200); i++) {
|
||||||
|
numbers.push(Math.floor(Math.random(400)))
|
||||||
|
}
|
||||||
const randomChoice = <T>(arr: T[]): T =>
|
const randomChoice = <T>(arr: T[]): T =>
|
||||||
arr[Math.floor(Math.random() * arr.length)];
|
arr[Math.floor(Math.random() * arr.length)];
|
||||||
|
|
||||||
@ -618,8 +625,7 @@ export class FakeShader {
|
|||||||
() => `${randomChoice(vars)}^${randomChoice(vars)}^${randomChoice(vars)}`,
|
() => `${randomChoice(vars)}^${randomChoice(vars)}^${randomChoice(vars)}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
// 70% chance to pick from presets, 30% chance to generate dynamic
|
if (Math.random() < 0.5) {
|
||||||
if (Math.random() < 0.7) {
|
|
||||||
return randomChoice(presets);
|
return randomChoice(presets);
|
||||||
} else {
|
} else {
|
||||||
return randomChoice(dynamicExpressions)();
|
return randomChoice(dynamicExpressions)();
|
||||||
|
|||||||
@ -62,6 +62,12 @@ class ShaderWorker {
|
|||||||
PERFORMANCE.COMPILATION_CACHE_SIZE
|
PERFORMANCE.COMPILATION_CACHE_SIZE
|
||||||
);
|
);
|
||||||
private feedbackBuffer: Float32Array | null = null;
|
private feedbackBuffer: Float32Array | null = null;
|
||||||
|
private previousFeedbackBuffer: Float32Array | null = null;
|
||||||
|
private stateBuffer: Float32Array | null = null;
|
||||||
|
private echoBuffers: Float32Array[] = [];
|
||||||
|
private echoFrameCounter: number = 0;
|
||||||
|
private echoInterval: number = 30; // Store echo every 30 frames (~0.5s at 60fps)
|
||||||
|
private lastFrameTime: number = 0;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
self.onmessage = (e: MessageEvent<WorkerMessage>) => {
|
self.onmessage = (e: MessageEvent<WorkerMessage>) => {
|
||||||
@ -139,6 +145,22 @@ class ShaderWorker {
|
|||||||
'd',
|
'd',
|
||||||
'n',
|
'n',
|
||||||
'b',
|
'b',
|
||||||
|
'bn',
|
||||||
|
'bs',
|
||||||
|
'be',
|
||||||
|
'bw',
|
||||||
|
'w',
|
||||||
|
'h',
|
||||||
|
'p',
|
||||||
|
'z',
|
||||||
|
'j',
|
||||||
|
'o',
|
||||||
|
'g',
|
||||||
|
'm',
|
||||||
|
'l',
|
||||||
|
'k',
|
||||||
|
's',
|
||||||
|
'e',
|
||||||
'mouseX',
|
'mouseX',
|
||||||
'mouseY',
|
'mouseY',
|
||||||
'mousePressed',
|
'mousePressed',
|
||||||
@ -201,7 +223,7 @@ class ShaderWorker {
|
|||||||
|
|
||||||
private isStaticExpression(code: string): boolean {
|
private isStaticExpression(code: string): boolean {
|
||||||
// Check if code contains any variables using regex for better accuracy
|
// Check if code contains any variables using regex for better accuracy
|
||||||
const variablePattern = /\b(x|y|t|i|r|a|u|v|c|f|d|n|b|bpm|mouse[XY]|mousePressed|mouseV[XY]|mouseClickTime|touchCount|touch[01][XY]|pinchScale|pinchRotation|accel[XYZ]|gyro[XYZ]|audioLevel|bassLevel|midLevel|trebleLevel)\b/;
|
const variablePattern = /\b(x|y|t|i|r|a|u|v|c|f|d|n|b|bn|bs|be|bw|m|l|k|s|e|w|h|p|z|j|o|g|bpm|mouse[XY]|mousePressed|mouseV[XY]|mouseClickTime|touchCount|touch[01][XY]|pinchScale|pinchRotation|accel[XYZ]|gyro[XYZ]|audioLevel|bassLevel|midLevel|trebleLevel)\b/;
|
||||||
|
|
||||||
return !variablePattern.test(code);
|
return !variablePattern.test(code);
|
||||||
}
|
}
|
||||||
@ -246,11 +268,23 @@ class ShaderWorker {
|
|||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
const maxRenderTime = PERFORMANCE.MAX_RENDER_TIME_MS;
|
const maxRenderTime = PERFORMANCE.MAX_RENDER_TIME_MS;
|
||||||
|
|
||||||
// Initialize feedback buffer if needed
|
// Initialize feedback buffers if needed
|
||||||
if (!this.feedbackBuffer || this.feedbackBuffer.length !== width * height) {
|
if (!this.feedbackBuffer || this.feedbackBuffer.length !== width * height) {
|
||||||
this.feedbackBuffer = new Float32Array(width * height);
|
this.feedbackBuffer = new Float32Array(width * height);
|
||||||
|
this.previousFeedbackBuffer = new Float32Array(width * height);
|
||||||
|
this.stateBuffer = new Float32Array(width * height);
|
||||||
|
|
||||||
|
// Initialize echo buffers (4 buffers for different time delays)
|
||||||
|
this.echoBuffers = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
this.echoBuffers.push(new Float32Array(width * height));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update frame timing for frame rate independence
|
||||||
|
const deltaTime = time - this.lastFrameTime;
|
||||||
|
this.lastFrameTime = time;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use tiled rendering for better timeout handling
|
// Use tiled rendering for better timeout handling
|
||||||
this.renderTiled(
|
this.renderTiled(
|
||||||
@ -263,8 +297,33 @@ class ShaderWorker {
|
|||||||
message,
|
message,
|
||||||
startTime,
|
startTime,
|
||||||
maxRenderTime,
|
maxRenderTime,
|
||||||
startY
|
startY,
|
||||||
|
deltaTime
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Copy current feedback to previous for next frame momentum calculations
|
||||||
|
if (this.feedbackBuffer && this.previousFeedbackBuffer) {
|
||||||
|
this.previousFeedbackBuffer.set(this.feedbackBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update echo buffers at regular intervals
|
||||||
|
this.echoFrameCounter++;
|
||||||
|
if (this.echoFrameCounter >= this.echoInterval && this.echoBuffers.length > 0) {
|
||||||
|
this.echoFrameCounter = 0;
|
||||||
|
|
||||||
|
// Rotate echo buffers: shift all buffers forward and store current in first buffer
|
||||||
|
for (let i = this.echoBuffers.length - 1; i > 0; i--) {
|
||||||
|
if (this.echoBuffers[i] && this.echoBuffers[i - 1]) {
|
||||||
|
this.echoBuffers[i].set(this.echoBuffers[i - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store current feedback in first echo buffer
|
||||||
|
if (this.feedbackBuffer && this.echoBuffers[0]) {
|
||||||
|
this.echoBuffers[0].set(this.feedbackBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.postMessage({ id, type: 'rendered', success: true, imageData });
|
this.postMessage({ id, type: 'rendered', success: true, imageData });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.postError(
|
this.postError(
|
||||||
@ -284,7 +343,8 @@ class ShaderWorker {
|
|||||||
message: WorkerMessage,
|
message: WorkerMessage,
|
||||||
startTime: number,
|
startTime: number,
|
||||||
maxRenderTime: number,
|
maxRenderTime: number,
|
||||||
yOffset: number = 0
|
yOffset: number = 0,
|
||||||
|
deltaTime: number = 0.016
|
||||||
): void {
|
): void {
|
||||||
const tileSize = PERFORMANCE.DEFAULT_TILE_SIZE;
|
const tileSize = PERFORMANCE.DEFAULT_TILE_SIZE;
|
||||||
const tilesX = Math.ceil(width / tileSize);
|
const tilesX = Math.ceil(width / tileSize);
|
||||||
@ -316,7 +376,8 @@ class ShaderWorker {
|
|||||||
renderMode,
|
renderMode,
|
||||||
valueMode,
|
valueMode,
|
||||||
message,
|
message,
|
||||||
yOffset
|
yOffset,
|
||||||
|
deltaTime
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,7 +394,8 @@ class ShaderWorker {
|
|||||||
renderMode: string,
|
renderMode: string,
|
||||||
valueMode: string,
|
valueMode: string,
|
||||||
message: WorkerMessage,
|
message: WorkerMessage,
|
||||||
yOffset: number = 0
|
yOffset: number = 0,
|
||||||
|
deltaTime: number = 0.016
|
||||||
): void {
|
): void {
|
||||||
// Get full canvas dimensions for special modes (use provided full dimensions or fall back)
|
// Get full canvas dimensions for special modes (use provided full dimensions or fall back)
|
||||||
const fullWidth = message.fullWidth || width;
|
const fullWidth = message.fullWidth || width;
|
||||||
@ -362,9 +424,63 @@ class ShaderWorker {
|
|||||||
const manhattanDistance =
|
const manhattanDistance =
|
||||||
Math.abs(x - centerX) + Math.abs(actualY - centerY);
|
Math.abs(x - centerX) + Math.abs(actualY - centerY);
|
||||||
const noise = (Math.sin(x * 0.1) * Math.cos(actualY * 0.1) + 1) * 0.5;
|
const noise = (Math.sin(x * 0.1) * Math.cos(actualY * 0.1) + 1) * 0.5;
|
||||||
const feedbackValue = this.feedbackBuffer
|
// Simple, efficient feedback system
|
||||||
? this.feedbackBuffer[pixelIndex] || 0
|
const currentFeedback = this.feedbackBuffer ? this.feedbackBuffer[pixelIndex] || 0 : 0;
|
||||||
: 0;
|
const feedbackValue = currentFeedback;
|
||||||
|
|
||||||
|
// Simple neighbor feedback with bounds checking
|
||||||
|
let fbn = 0, fbs = 0, fbe = 0, fbw = 0;
|
||||||
|
if (this.feedbackBuffer) {
|
||||||
|
// North neighbor (bounds safe)
|
||||||
|
if (y > 0) fbn = this.feedbackBuffer[(y - 1) * width + x] || 0;
|
||||||
|
// South neighbor (bounds safe)
|
||||||
|
if (y < fullHeight - 1) fbs = this.feedbackBuffer[(y + 1) * width + x] || 0;
|
||||||
|
// East neighbor (bounds safe)
|
||||||
|
if (x < width - 1) fbe = this.feedbackBuffer[y * width + (x + 1)] || 0;
|
||||||
|
// West neighbor (bounds safe)
|
||||||
|
if (x > 0) fbw = this.feedbackBuffer[y * width + (x - 1)] || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate feedback-based operators
|
||||||
|
// m - Momentum/Velocity (change from previous frame)
|
||||||
|
const previousValue = this.previousFeedbackBuffer ? this.previousFeedbackBuffer[pixelIndex] || 0 : 0;
|
||||||
|
const momentum = (feedbackValue - previousValue) * 0.5; // Scale for stability
|
||||||
|
|
||||||
|
// l - Laplacian/Diffusion (spatial derivative)
|
||||||
|
const laplacian = (fbn + fbs + fbe + fbw - feedbackValue * 4) * 0.25;
|
||||||
|
|
||||||
|
// k - Curvature/Contrast (gradient magnitude)
|
||||||
|
const gradientX = (fbe - fbw) * 0.5;
|
||||||
|
const gradientY = (fbs - fbn) * 0.5;
|
||||||
|
const curvature = Math.sqrt(gradientX * gradientX + gradientY * gradientY);
|
||||||
|
|
||||||
|
// s - State/Memory (persistent accumulator)
|
||||||
|
let currentState = this.stateBuffer ? this.stateBuffer[pixelIndex] || 0 : 0;
|
||||||
|
// State accumulates when feedback is high, decays when low
|
||||||
|
if (feedbackValue > 128) {
|
||||||
|
currentState = Math.min(255, currentState + deltaTime * 200); // Accumulate
|
||||||
|
} else {
|
||||||
|
currentState = Math.max(0, currentState - deltaTime * 100); // Decay
|
||||||
|
}
|
||||||
|
const stateValue = currentState;
|
||||||
|
|
||||||
|
// e - Echo/History (temporal snapshots)
|
||||||
|
let echoValue = 0;
|
||||||
|
if (this.echoBuffers.length > 0) {
|
||||||
|
// Cycle through different echo delays based on time
|
||||||
|
const echoIndex = Math.floor(time * 2) % this.echoBuffers.length; // Change every 0.5 seconds
|
||||||
|
const echoBuffer = this.echoBuffers[echoIndex];
|
||||||
|
echoValue = echoBuffer ? echoBuffer[pixelIndex] || 0 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate other variables
|
||||||
|
const canvasWidth = fullWidth;
|
||||||
|
const canvasHeight = fullHeight;
|
||||||
|
const phase = (time * Math.PI * 2) % (Math.PI * 2); // 0 to 2π cycling
|
||||||
|
const pseudoZ = Math.sin(radius * 0.01 + time) * 50; // depth based on radius and time
|
||||||
|
const jitter = ((x * 73856093 + actualY * 19349663) % 256) / 255; // deterministic per-pixel random
|
||||||
|
const oscillation = Math.sin(time * 2 * Math.PI + radius * 0.1); // wave oscillation
|
||||||
|
const goldenRatio = 1.618033988749; // golden ratio constant
|
||||||
|
|
||||||
const value = this.compiledFunction!(
|
const value = this.compiledFunction!(
|
||||||
x,
|
x,
|
||||||
@ -380,6 +496,22 @@ class ShaderWorker {
|
|||||||
manhattanDistance,
|
manhattanDistance,
|
||||||
noise,
|
noise,
|
||||||
feedbackValue,
|
feedbackValue,
|
||||||
|
canvasWidth,
|
||||||
|
canvasHeight,
|
||||||
|
phase,
|
||||||
|
pseudoZ,
|
||||||
|
jitter,
|
||||||
|
oscillation,
|
||||||
|
goldenRatio,
|
||||||
|
momentum,
|
||||||
|
laplacian,
|
||||||
|
curvature,
|
||||||
|
stateValue,
|
||||||
|
echoValue,
|
||||||
|
fbn,
|
||||||
|
fbs,
|
||||||
|
fbe,
|
||||||
|
fbw,
|
||||||
message.mouseX || 0,
|
message.mouseX || 0,
|
||||||
message.mouseY || 0,
|
message.mouseY || 0,
|
||||||
message.mousePressed ? 1 : 0,
|
message.mousePressed ? 1 : 0,
|
||||||
@ -422,9 +554,28 @@ class ShaderWorker {
|
|||||||
data[i + 2] = b;
|
data[i + 2] = b;
|
||||||
data[i + 3] = 255;
|
data[i + 3] = 255;
|
||||||
|
|
||||||
// Update feedback buffer with current processed value
|
// Store feedback as luminance of displayed color for consistency
|
||||||
if (this.feedbackBuffer) {
|
if (this.feedbackBuffer) {
|
||||||
this.feedbackBuffer[pixelIndex] = safeValue;
|
// Use the actual displayed luminance as feedback (0-255 range)
|
||||||
|
const luminance = (r * 0.299 + g * 0.587 + b * 0.114);
|
||||||
|
|
||||||
|
// Frame rate independent decay
|
||||||
|
const decayFactor = Math.pow(0.95, deltaTime * 60); // 5% decay at 60fps
|
||||||
|
|
||||||
|
// Simple mixing to prevent oscillation
|
||||||
|
const previousValue = this.feedbackBuffer[pixelIndex] || 0;
|
||||||
|
const mixRatio = Math.min(deltaTime * 10, 0.3); // Max 30% new value per frame
|
||||||
|
|
||||||
|
let newFeedback = luminance * mixRatio + previousValue * (1 - mixRatio);
|
||||||
|
newFeedback *= decayFactor;
|
||||||
|
|
||||||
|
// Clamp and store
|
||||||
|
this.feedbackBuffer[pixelIndex] = Math.max(0, Math.min(255, newFeedback));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state buffer
|
||||||
|
if (this.stateBuffer) {
|
||||||
|
this.stateBuffer[pixelIndex] = stateValue;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
data[i] = 0;
|
data[i] = 0;
|
||||||
@ -584,8 +735,8 @@ class ShaderWorker {
|
|||||||
case 'cellular': {
|
case 'cellular': {
|
||||||
// Cellular automata-inspired patterns
|
// Cellular automata-inspired patterns
|
||||||
const cellSize = 16;
|
const cellSize = 16;
|
||||||
const cellX = Math.floor(x / cellSize);
|
const cellX = Math.floor(x / centerX);
|
||||||
const cellY = Math.floor(y / cellSize);
|
const cellY = Math.floor(y / centerY);
|
||||||
const cellHash =
|
const cellHash =
|
||||||
(cellX * 73856093) ^ (cellY * 19349663) ^ Math.floor(Math.abs(value));
|
(cellX * 73856093) ^ (cellY * 19349663) ^ Math.floor(Math.abs(value));
|
||||||
|
|
||||||
@ -803,7 +954,7 @@ class ShaderWorker {
|
|||||||
const gridX = Math.floor(x / latticeSize);
|
const gridX = Math.floor(x / latticeSize);
|
||||||
const gridY = Math.floor(y / latticeSize);
|
const gridY = Math.floor(y / latticeSize);
|
||||||
const crystal = Math.sin(gridX + gridY + Math.abs(value) * 0.01) *
|
const crystal = Math.sin(gridX + gridY + Math.abs(value) * 0.01) *
|
||||||
Math.cos(gridX * gridY + Math.abs(value) * 0.005);
|
Math.cos(gridX * gridY + Math.abs(value) * 0.005);
|
||||||
processedValue = Math.floor((crystal * 0.5 + 0.5) * 255);
|
processedValue = Math.floor((crystal * 0.5 + 0.5) * 255);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -812,7 +963,7 @@ class ShaderWorker {
|
|||||||
// Marble-like veining patterns
|
// Marble-like veining patterns
|
||||||
const noiseFreq = 0.005 + Math.abs(value) * 0.00001;
|
const noiseFreq = 0.005 + Math.abs(value) * 0.00001;
|
||||||
const turbulence = Math.sin(x * noiseFreq) * Math.cos(y * noiseFreq) +
|
const turbulence = Math.sin(x * noiseFreq) * Math.cos(y * noiseFreq) +
|
||||||
Math.sin(x * noiseFreq * 2) * Math.cos(y * noiseFreq * 2) * 0.5;
|
Math.sin(x * noiseFreq * 2) * Math.cos(y * noiseFreq * 2) * 0.5;
|
||||||
const marble = Math.sin((x + turbulence * 50) * 0.02 + Math.abs(value) * 0.001);
|
const marble = Math.sin((x + turbulence * 50) * 0.02 + Math.abs(value) * 0.001);
|
||||||
processedValue = Math.floor((marble * 0.5 + 0.5) * 255);
|
processedValue = Math.floor((marble * 0.5 + 0.5) * 255);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -52,21 +52,21 @@ export function HelpPopup() {
|
|||||||
<strong>M</strong> - Cycle value mode
|
<strong>M</strong> - Cycle value mode
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Space</strong> - Tap tempo (when editor not focused)
|
<strong>Space</strong> - Tap tempo
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Arrow Left/Right</strong> - Adjust hue shift (when editor not focused)
|
<strong>Arrow Left/Right</strong> - Adjust hue shift
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Arrow Up/Down</strong> - Cycle value mode (when editor not focused)
|
<strong>Arrow Up/Down</strong> - Cycle value mode
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Shift+Arrow Up/Down</strong> - Cycle render mode (when editor not focused)
|
<strong>Shift+Arrow Up/Down</strong> - Cycle render mode
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="help-section">
|
<div className="help-section">
|
||||||
<h4>Variables</h4>
|
<h4>Core Variables - Basics</h4>
|
||||||
<p>
|
<p>
|
||||||
<strong>x, y</strong> - Pixel coordinates
|
<strong>x, y</strong> - Pixel coordinates
|
||||||
</p>
|
</p>
|
||||||
@ -97,11 +97,30 @@ export function HelpPopup() {
|
|||||||
<p>
|
<p>
|
||||||
<strong>d</strong> - Manhattan distance from center
|
<strong>d</strong> - Manhattan distance from center
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>w, h</strong> - Canvas width and height (pixels)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="help-section">
|
||||||
|
<h4>Core Variables - Advanced</h4>
|
||||||
<p>
|
<p>
|
||||||
<strong>n</strong> - Noise value (0.0 to 1.0)
|
<strong>n</strong> - Noise value (0.0 to 1.0)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>b</strong> - Previous frame's value (feedback)
|
<strong>p</strong> - Phase value (0 to 2π, cycles with time)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>z</strong> - Pseudo-depth coordinate (oscillates with distance and time)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>j</strong> - Per-pixel jitter/random value (0.0 to 1.0, deterministic)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>o</strong> - Oscillation value (wave function based on time and distance)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>g</strong> - Golden ratio constant (1.618... for natural spirals)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>mouseX, mouseY</strong> - Mouse position (0.0 to 1.0)
|
<strong>mouseX, mouseY</strong> - Mouse position (0.0 to 1.0)
|
||||||
@ -117,6 +136,34 @@ export function HelpPopup() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="help-section">
|
||||||
|
<h4>Feedback Variables</h4>
|
||||||
|
<p>
|
||||||
|
<strong>b</strong> - Previous frame's luminance at this pixel (0-255)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>bn, bs, be, bw</strong> - Neighbor luminance (North, South, East, West)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>m</strong> - Momentum/velocity: Detects motion and change between frames
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>l</strong> - Laplacian/diffusion: Creates natural spreading and heat diffusion
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>k</strong> - Curvature/contrast: Edge detection and gradient magnitude
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>s</strong> - State/memory: Persistent accumulator that remembers bright areas
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>e</strong> - Echo/history: Temporal snapshots that recall past brightness patterns
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<em>Feedback uses actual displayed brightness with natural decay and frame-rate independence for stable, evolving patterns.</em>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="help-section">
|
<div className="help-section">
|
||||||
<h4>Touch & Gestures</h4>
|
<h4>Touch & Gestures</h4>
|
||||||
<p>
|
<p>
|
||||||
@ -214,29 +261,6 @@ export function HelpPopup() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="help-section">
|
|
||||||
<h4>Value Modes</h4>
|
|
||||||
<p>
|
|
||||||
<strong>Integer (0-255):</strong> Traditional mode for large values
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Float (0.0-1.0):</strong> Bitfield shader mode, inverts and
|
|
||||||
clamps values
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Polar (angle-based):</strong> Spiral patterns combining
|
|
||||||
angle and radius
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Distance (radial):</strong> Concentric wave rings with
|
|
||||||
variable frequency
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Wave (ripple):</strong> Multi-source interference with
|
|
||||||
amplitude falloff
|
|
||||||
</p>
|
|
||||||
<p>Each mode transforms your expression differently!</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="help-section">
|
<div className="help-section">
|
||||||
<h4>Advanced Features</h4>
|
<h4>Advanced Features</h4>
|
||||||
@ -261,40 +285,22 @@ export function HelpPopup() {
|
|||||||
<div className="help-section">
|
<div className="help-section">
|
||||||
<h4>Shader Library</h4>
|
<h4>Shader Library</h4>
|
||||||
<p>
|
<p>
|
||||||
Hover over the <strong>left edge</strong> of the screen to access
|
<strong>Access:</strong> Hover over the left edge of the screen
|
||||||
the shader library
|
|
||||||
</p>
|
</p>
|
||||||
<p>Save shaders with custom names and search through them</p>
|
|
||||||
<p>
|
<p>
|
||||||
Use <strong>edit</strong> to rename, <strong>del</strong> to delete
|
<strong>Save:</strong> Click the save icon to store current shader
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Search:</strong> Filter saved shaders by name
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Manage:</strong> Edit names or delete with the buttons
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Load:</strong> Click any shader to apply it instantly
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="help-section">
|
|
||||||
<h4>Render Modes</h4>
|
|
||||||
<p>
|
|
||||||
<strong>Classic</strong> - Original colorful mode
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Grayscale</strong> - Black and white
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Red/Green/Blue</strong> - Single color channels
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>HSV</strong> - Hue-based coloring
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Rainbow</strong> - Spectrum coloring
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="help-section">
|
|
||||||
<h4>Export</h4>
|
|
||||||
<p>
|
|
||||||
<strong>Export PNG</strong> - Save current frame as image
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user