/** * matrix_button.ts - Adds a "Show Matrix" documentation button to ShaderNoiseKSampler nodes * Displays interactive shader documentation modal with noise visualizations */ // @ts-ignore - Runtime ComfyUI import import { app } from "../../../scripts/app.js"; // === INITIALIZATION === (function () { // Define utility functions on window object to be accessible by treatiseHTML window.scrollToSection = function (sectionId) { const modalContent = document.querySelector('.shader-matrix-treatise'); if (!modalContent) return; const section = modalContent.querySelector('#' + sectionId); if (section) { section.scrollIntoView({ behavior: 'smooth' }); // UX Enhancement: Move focus to the section header for accessibility const header = section.querySelector('h1, h2, h3, h4, h5, h6'); if (header) { // Ensure the header is focusable programmatically if (!header.hasAttribute('tabindex')) { header.setAttribute('tabindex', '-1'); } // Focus the header to update reading position for keyboard/screen reader users header.focus({ preventScroll: true }); } } }; window.showTab = function (tabIdToActivate, clickedTabElement) { let tabsContainer = null; let contentScope = null; let tabSelector = ''; let tabContentSelector = ''; if (clickedTabElement) { tabsContainer = clickedTabElement.closest('.tabs') || clickedTabElement.closest('.sacred-tabs'); } else { const modalDiv = document.querySelector('.shader-matrix-treatise'); if (!modalDiv) return; tabsContainer = modalDiv.querySelector('.tabs') || modalDiv.querySelector('.sacred-tabs'); } if (!tabsContainer) { console.error("showTab: Could not find '.tabs' or '.sacred-tabs' container."); return; } if (tabsContainer.classList.contains('tabs')) { tabSelector = '.tab'; tabContentSelector = '.tab-content'; contentScope = tabsContainer.parentNode; } else if (tabsContainer.classList.contains('sacred-tabs')) { tabSelector = '.sacred-tab'; tabContentSelector = '.sacred-tab-content'; contentScope = tabsContainer.closest('.sacred-section') || tabsContainer.parentNode; } else { return; } tabsContainer.querySelectorAll(tabSelector).forEach(tab => { tab.classList.remove('active'); tab.setAttribute('aria-selected', 'false'); tab.setAttribute('tabindex', '-1'); }); let activeTab = clickedTabElement; if (!activeTab) { activeTab = Array.from(tabsContainer.querySelectorAll(tabSelector)).find(t => t.getAttribute('onclick')?.includes(tabIdToActivate)); } if (activeTab) { activeTab.classList.add('active'); activeTab.setAttribute('aria-selected', 'true'); activeTab.setAttribute('tabindex', '0'); } if (contentScope) { contentScope.querySelectorAll(tabContentSelector).forEach(content => { content.style.display = 'none'; content.classList.remove('active'); }); const activeContent = contentScope.querySelector('#' + tabIdToActivate); if (activeContent) { activeContent.style.display = 'block'; activeContent.classList.add('active'); } } }; window.setupScrollTop = function (modalContentElement) { if (!modalContentElement) return; const scrollTopButton = modalContentElement.querySelector('#scroll-top'); const titleElement = modalContentElement.querySelector('#treatise-title'); const modalEl = modalContentElement; if (scrollTopButton) { modalEl.addEventListener('scroll', () => { if (modalEl.scrollTop > 200) { scrollTopButton.classList.add('visible'); } else { scrollTopButton.classList.remove('visible'); } }); scrollTopButton.addEventListener('click', (e) => { e.stopPropagation(); modalEl.scrollTo({ top: 0, behavior: 'smooth' }); if (titleElement) { titleElement.focus({ preventScroll: true }); } }); } }; // Helper to reset copy button state after timeout const resetButtonTimeout = (button) => { if (button.dataset.timeoutId) { clearTimeout(parseInt(button.dataset.timeoutId)); } const timeoutId = setTimeout(() => { button.textContent = "Copy"; button.classList.remove('copied'); // Restore aria-label const originalLabel = button.dataset.originalAriaLabel; if (originalLabel) { button.setAttribute('aria-label', originalLabel); delete button.dataset.originalAriaLabel; } delete button.dataset.timeoutId; }, 2000); button.dataset.timeoutId = String(timeoutId); }; window.copyCodeSection = function (buttonElement) { const headerElement = buttonElement.closest('.code-block-header'); if (!headerElement) return; const codeBlockContainer = headerElement.parentNode; if (!codeBlockContainer) return; const preElement = codeBlockContainer.querySelector('pre.foldable-content code'); if (!preElement) return; const codeText = preElement.textContent || ''; navigator.clipboard.writeText(codeText).then(() => { if (window.showComfyToast) { window.showComfyToast("Code copied to clipboard!", "success"); } // Store original label if not already stored if (!buttonElement.dataset.originalAriaLabel) { buttonElement.dataset.originalAriaLabel = buttonElement.getAttribute('aria-label') || 'Copy code'; } buttonElement.textContent = "Copied!"; buttonElement.classList.add('copied'); buttonElement.setAttribute('aria-label', 'Copied'); // Update for screen readers resetButtonTimeout(buttonElement); }).catch(err => { console.error('Failed to copy: ', err); if (window.showComfyToast) { window.showComfyToast("Failed to copy code.", "error"); } buttonElement.textContent = "Error"; resetButtonTimeout(buttonElement); }); }; window.toggleCodeSection = function (buttonElement) { const headerElement = buttonElement.closest('.code-block-header'); if (!headerElement) return; const codeBlockContainer = headerElement.parentNode; if (!codeBlockContainer) return; const preElement = codeBlockContainer.querySelector('pre.foldable-content'); if (!preElement) return; const isHidden = preElement.style.display === 'none' || preElement.style.display === ''; if (isHidden) { preElement.style.display = 'block'; buttonElement.textContent = 'Hide'; buttonElement.setAttribute('aria-expanded', 'true'); } else { preElement.style.display = 'none'; buttonElement.textContent = 'Show'; buttonElement.setAttribute('aria-expanded', 'false'); } }; window.handleTabNavigation = function (event, tabElement) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); tabElement.click(); return; } if (!tabElement.parentElement) return; const tabs = Array.from(tabElement.parentElement.children).filter((child) => child.classList.contains('tab')); const index = tabs.indexOf(tabElement); let nextIndex = -1; if (event.key === 'ArrowRight' || event.key === 'ArrowDown') { nextIndex = (index + 1) % tabs.length; } else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') { nextIndex = (index - 1 + tabs.length) % tabs.length; } if (nextIndex !== -1) { event.preventDefault(); const nextTab = tabs[nextIndex]; nextTab.focus(); nextTab.click(); } }; // Register the extension for ShaderDisplay and ShaderNoiseKSampler nodes app.registerExtension({ name: "ComfyUI.ShaderNoise.MatrixButton", beforeRegisterNodeDef(nodeType, nodeData) { // Modify ShaderDisplay, ShaderNoiseKSampler, and ShaderNoiseKSamplerDirect nodes if (nodeData.name !== "ShaderDisplay" && nodeData.name !== "ShaderNoiseKSampler" && nodeData.name !== "ShaderNoiseKSamplerDirect") { return; } // Store the original methods const originalOnNodeCreated = nodeType.prototype.onNodeCreated; // Add our button to the node nodeType.prototype.onNodeCreated = function () { // Call the original onNodeCreated method first const self = this; // Node instance if (originalOnNodeCreated) { originalOnNodeCreated.apply(self, arguments); } // Add the "Show Matrix" button widget // const self = this; // self is already defined above // Function to add the matrix button const addMatrixButton = () => { // MODIFIED: HTML content from Treatise.js // window.showTab_SacredCodex = function(tabId, clickedTabElement) { ... }; // This ^ is now replaced by the global window.showTab for new HTML, // but can be kept if old structures outside this modal might use it. // For this modal, the new HTML will use window.showTab. const button = self.addWidget("button", "📊 Show Shader Matrix", null, function () { // Create modal container const modal = document.createElement("div"); // Accessibility attributes for the modal modal.setAttribute('role', 'dialog'); modal.setAttribute('aria-modal', 'true'); modal.setAttribute('aria-label', 'Shader Matrix Documentation'); modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(135deg, rgba(0,0,0,0.9), rgba(26,13,52,0.95)); display: flex; justify-content: center; align-items: center; z-index: 10000; backdrop-filter: blur(5px); `; let handleEscPress = null; const closeModalCleanup = () => { if (modal && modal.parentNode) { document.body.removeChild(modal); } if (handleEscPress) { document.removeEventListener('keydown', handleEscPress); } // UX Enhancement: Restore focus to the main canvas const appCanvas = app.canvas?.canvas; // Access the DOM element if (appCanvas instanceof HTMLElement) { appCanvas.focus(); } }; handleEscPress = (e) => { if (e.key === "Escape") { closeModalCleanup(); } }; document.addEventListener('keydown', handleEscPress); modal.onclick = (e) => { if (e.target === modal) { closeModalCleanup(); } }; const content = document.createElement("div"); content.className = "shader-matrix-treatise"; // This is the main scrollable container content.style.cssText = ` position: relative; /* For positioning of child elements like scroll-to-top */ background: linear-gradient(145deg, #0f0f12, #1a1a2e); /* Match new body bg */ border-radius: 16px; padding: 0; /* Remove padding, let content manage it */ max-width: 95vw; width: 1400px; /* Wider for new content */ max-height: 90vh; overflow-y: auto; color: #e0e0e8; /* Match new text color */ box-shadow: 0 0 60px rgba(138, 43, 226, 0.5); border: 1px solid rgba(138, 43, 226, 0.4); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* Base font */ line-height: 1.6; `; // MODIFIED: HTML content from Treatise.js const treatiseHTML = `

The Shader Matrix

Harnessing Noise with Shader Algorithms for Image Generation

🔮 Introduction ✨ The Essence of Noise

ShaderNoiseKSampler is a ComfyUI node that uses shader algorithms to generate and control procedural noise. This noise, refeered to as shader noise, can then be used to influence the AI image creation process in latent diffusion models. The aim is to provide a way to introduce structured mathematical patterns into the diffusion process, offering a different approach to guiding image generation.

This document outlines the features, technical details, and underlying ideas of ShaderNoiseKSampler, showing how different shader noise types can create various visual characteristics that influence and drive the diffusion process.

💠 The Path of Noise

Noise is fundamentally a mathematical expression of ordered chaos—a seemingly random pattern that nonetheless follows precise mathematical rules. In the context of image generation, these noise patterns serve as the seed from which creation emerges. ShaderNoiseKSampler allows artists to shape this primordial mathematical chaos according to sacred geometric principles, guiding the diffusion model\'s sampling process along specific aesthetic trajectories.

Just as ancient civilizations recognized patterns in nature and encoded them in their art and architecture, ShaderNoiseKSampler enables digital artists to encode mathematical archetypes into the generative process, creating a bridge between abstract mathematics and visual expression.

🐜 Project Genesis ∞The Birth of Order from Chaos

👁️ The Vision Unveiled

The genesis of ShaderNoiseKSampler lies in a contemplation of nature\'s fundamental paradox: the inherent order within apparent chaos. Observing the natural world reveals that seemingly random phenomena—the flicker of fire, the branching of lightning, the flow of water, the structure of terrestrial forms—all exhibit underlying fractal patterns. This ubiquitous principle, that profound order underpins apparent disorder, became a guiding inspiration.

The initial conceptual spark was hyperspace—an unseen realm between dimensions. While seemingly chaotic in its construct, closer examination reveals a perfect, almost unimaginable mathematical precision and scope. It is this notion that the unseen can be rendered observable with the appropriate tools which led to a pivotal question: Could a method be devised to introduce deliberate order into random noise? Could one inject structure into the latent space itself, thereby crystallizing defined pathways for creative generation?

🏗️ The Ant Colony Revelation

A compelling metaphor emerged from an unexpected source: the art of ant sculpture. This process, which captures the hidden architecture of ant tunnels by filling them with molten metal, offered a tangible analogy. Once cooled and excavated, the metal cast reveals a previously unseen, complex structure—a physical manifestation of the colony\'s hidden pathways. The question then became: how could one create a virtual equivalent of this molten metal? The answer was clear: Shaders.

  • Virtual Molten Metal (Shaders) = Shader Noise
    Shader patterns, like molten metal, flow into and define the pathways within latent space.
  • Solidification = Sampling Process
    The K-sampling process allows this structured noise to effectively \'solidify\' within the latent dimensions.
  • Excavation = Denoising
    As the model denoises, it progressively reveals the crystallized patterns embedded by the shader.
  • Revealed Sculpture = Final Image
    The output is a manifestation of a previously unseen space, a navigable pathway forged through the realm of possibility.
  • ⚗️ The Alchemical Process: From Theory to Practice

    The journey from concept to a functional implementation involved extensive experimentation, iterative debugging, and countless visualization tests. The challenge was multifaceted, encompassing not only technical hurdles but also philosophical considerations: How could the abstract vision of controlled chaos be translated into robust code? Critically, how could novel structures be introduced without disrupting the model\'s foundational training?

    A crucial breakthrough was the understanding that models cannot be expected to produce coherent results when fed noise types entirely alien to their training data. This led to the principle of augmentation rather than replacement. The final noise input to the KSampler is therefore not a wholesale substitution but a careful modification of the standard base noise. The shader noise augments and sculpts this base noise, and the influence of these shader-defined patterns began to yield compelling and controllable results in the generated outputs.

    This hybrid methodology—a delicate equilibrium between the familiar (the model\'s learned representations) and the novel (the intentionally introduced structure)—fosters both generative stability and exploratory capacity.

    The Blend: A Cornerstone Principle

    Respecting a model\'s training by blending shader-generated noise with the base noise—as opposed to outright replacement—is a cornerstone of ShaderNoiseKSampler. This approach preserves generative coherence while enabling nuanced, controllable structural influence.

    "Not to replace the chaos, but to give it direction
    Not to eliminate randomness, but to make it purposeful
    To transform noise into navigation, and navigation into art"
    — The Genesis Vision

    🗺️ Exploring Latent Space: A Developing Idea

    The exploration extended to the nature of latent space. It became apparent that by adjusting shader noise parameters with a consistent seed, one could systematically alter the generative output. This suggested a way to modify not just noise characteristics, but potentially the "topology" of the latent space being explored.

    This led to an interesting mode of exploration. It was observed that different seeds seemed to contain unique thematic elements—recurring characters, color palettes, objects, and styles—that could be traced and explored. ShaderNoiseKSampler evolved from a noise tool into a means of navigating within a model's latent space with more specific control. This helped clarify the tool's potential.

    Each parameter adjustment can be seen as a step along conceptual pathways, each blend mode a different way to perceive and traverse the possibilities.

    🌌 Core Concept △ The Alchemical Transformation

    ShaderNoiseKSampler implements the formula Lt=Sα(N)∘Kβ(t)—a mathematical invocation where structured noise Sα transforms the base noise N before it enters the diffusion sampling process Kβ. This elegant formula captures the essence of guided chaos, where randomness is structured according to sacred patterns before manifesting in the final image.

    The Mathematics of Transformation

    The core equation Lt=Sα(N)∘Kβ(t) can be understood as follows:

    Lt = Sα(N) ∘ Kβ(t)

    Where:

    • Lt: The final latent representation at timestep t
    • Sα(N): The shader transformation with parameters α applied to base noise N
    • Kβ(t): The diffusion sampling process with parameters β at timestep t
    • ∘: Function composition operator (application of one function to the result of another)

    This mathematical formulation allows for more precise control over the noise structures that guide the generative process, creating a harmony between randomness and order.

    ⚙️ The Role of ShaderToTensor: Bridging Code and Concept

    The ShaderToTensor class, implemented in shader_to_tensor.py, is the cornerstone of this system\'s ability to translate abstract shader concepts into concrete PyTorch tensors. It acts as the alchemical crucible where mathematical descriptions of noise are transmuted into the actual structured noise (Sα in our formula) that the KSampler can utilize.

    Key functions of ShaderToTensor include:

    • Noise Synthesis: It contains the Python and PyTorch implementations of various noise algorithms (Perlin, Cellular, Curl, etc.). When you select a \'Shader Noise Type\' in the UI, you\'re choosing a specific method within this class.
    • Parameter Interpretation: It takes the parameters you set (Scale, Octaves, Warp Strength, etc.) and uses them to control the generation of these noise tensors.
    • Tensor Formatting: It ensures the generated noise is in the correct format (shape, data type, device) to be compatible with the ComfyUI latent workflow and the KSampler. This includes handling channel expansion (e.g., from 1-channel noise to 4 or 9 channels for the latent space).
    • Temporal Coherence Logic: For animated noise, ShaderToTensor (often in conjunction with specialized generator classes) implements the logic to produce evolving noise patterns over a time dimension.

    Essentially, ShaderToTensor is the engine that powers the Sα(N) part of the equation. Without it, the conceptual shader patterns would remain abstract; this class makes them tangible and usable by the diffusion model.

    📊 Essential Parameters

    Shader Noise Type

    The fundamental pattern archetype (tensor_field, curl_noise, domain_warp, etc.)

    Noise Scale

    Controls the frequency of pattern repetition (1.0 is the harmonic baseline)

    Noise Octaves

    The number of recursive self-similar layers (follows Fibonacci principles)

    Noise Warp Strength

    Controls the amount of displacement or distortion applied to the noise coordinates, altering the pattern's structure.

    Noise Phase Shift

    Adjusts the input to the noise function, effectively shifting the generated pattern. This can be used to explore variations of the noise.

    Shape Mask Type

    The geometric form overlaid upon the noise (radial, spiral, hexgrid, etc.)

    Shape Mask Strength

    Controls the intensity of the shape mask, determining how much of the noise is affected by the mask.

    Noise Transformation

    Mathematical operations (e.g., absolute, sin, sqrt) applied to the shader noise before blending. [Recommended: Experiment freely. See 'Noise Transformations' section for details].

    Blend Mode

    The alchemical method of combining shader noise with base noise (multiply, add, etc.). [Recommended: Multiply, Normal, Screen, Overlay].

    Color Intensity

    Adjusts the impact of the selected color scheme on the noise pattern.

    Sequential Stages

    Number of sequential shader stages to apply before injection stages. Each stage can have varied strength and apply noise over a specific portion of the diffusion steps. [Recommended: 1 - 3].

    Injection Stages

    Number of injection shader stages to apply after sequential stages. These stages typically inject noise at specific steps within the diffusion process, allowing for targeted interventions. [Recommended: 0 - 2].

    Shader Strength

    Overall strength of the shader noise influence for all stage types. Set to 0.0 to disable shader noise and use only the base noise. This acts as a global multiplier for stage-specific strengths. [Recommended: 0.1 - 0.4].

    CFG

    Classifier-Free Guidance scale. [Recommended: 6 - 9].

    Steps

    Number of sampling iterations. [Recommended: 20 - 60].

    Sampler & Scheduler Compatibility
    Sampler
    Compatibility
    Notes
    Euler_Ancestral
    High
    Excellent across all model types
    dpm_2_ancestral
    High
    Strong performance with all models
    dpmppm_2_ancestral
    High
    Reliable for all model variants
    LCM
    High
    Consistent results across models
    Scheduler
    Compatibility
    Notes
    Beta
    High
    Recommended scheduler
    Normal
    High
    Good general performance
    Simple
    High
    Effective for most cases
    kl_optimal
    High
    Optimized performance

    🏵️ Shader Noise Patterns ✧ The Twelve Noise Archetypes

    ShaderNoiseKSampler harnesses twelve fundamental shader noise archetypes, each channeling a different aspect of mathematical reality. These patterns are the building blocks of visual coherence, from the flowing whorls of Perlin noise to the cosmic web-like structures of Tensor Fields. Each archetype generates its own distinct style of noise, offering a diverse palette for visual expression.

    Tensor Field
    Manifests flow patterns based on tensor mathematics, revealing the underlying force vectors of the mathematical space.
    Cellular
    Creates organic, cell-like structures through Voronoi diagrams, mimicking natural growth patterns.
    Domain Warp
    Applies non-linear distortions to space itself, creating fluid-like transformations of the underlying pattern.
    Fractal
    Embodies self-similarity across scales, reflecting the infinite recursive patterns found throughout nature.
    Perlin
    The classic gradient noise that creates smooth, natural-looking transitions and flowing textures.
    Waves
    Generates harmonic oscillations that combine to form complex interference patterns.
    Gaussian
    Pure probabilistic noise based on the normal distribution, the foundation of natural randomness.
    Heterogeneous FBM
    Varies the fractal dimension across space, creating regions of different turbulence and detail.
    Interference
    Simulates wave interaction patterns, creating complex nodal structures through phase relationships.
    Spectral
    Controls the frequency spectrum directly, allowing precise frequency band manipulation.
    3D Projection
    Projects three-dimensional noise onto a 2D plane, creating depth and volumetric effects.
    Curl Noise
    Generates divergence-free vector fields that create perfect fluid-like flow patterns.

    ⚛️ Noise Mathematics 📐 The Underlying Formulations

    Each shader noise archetype in ShaderNoiseKSampler is built upon rigorous mathematical foundations. Understanding these mathematical principles reveals the sacred geometry inherent in these patterns and allows for more intentional application in the creative process.

    Perlin Noise Mathematics

    Perlin noise, developed by Ken Perlin in 1983, uses a grid of random gradient vectors with interpolation to create smooth, natural-looking noise. The core computation involves:

    n(x,y) = ∑ ωi · gi · ((x,y) - (xi,yi))

    Where gi are random gradient vectors at grid points, and ωi are interpolation weights. The smoothstep function provides the basis for the interpolation:

    smoothstep(t) = t2(3 - 2t)

    This smooth interpolation creates the characteristic flowing appearance of Perlin noise, making it ideal for natural phenomena like terrain, clouds, and flowing water.

    Perlin Noise Implementation

    Cellular Noise Mathematics

    Cellular noise (also known as Worley noise) creates Voronoi-like patterns based on distance metrics to feature points:

    Fn(x) = nth min{dist(x, xi) | i ∈ feature points}

    Where dist() is a distance function (typically Euclidean) and Fn returns the distance to the nth closest feature point. Various cellular patterns emerge by combining these distances:

    F2 - F1 : cell edges
    F1 : cell interiors
    2F1 - F2 : cracks

    These combinations create patterns reminiscent of cellular structures, bubbles, and organic tissues.

    Cellular Noise Implementation

    Tensor Field Mathematics

    Tensor fields represent directional information at every point in space, creating flow-like patterns. The core mathematics involves computing the eigenvalues λ and eigenvectors v of tensor matrices:

    T(x,y) = \\begin{pmatrix} T_{xx} & T_{xy} \\\\ T_{xy} & T_{yy} \\end{pmatrix}

    The eigenvalues and eigenvectors are found by solving:

    T·v = λ·v

    Different visualizations emerge based on how we render these tensor properties:

    • Eigenvalue visualization: Shows magnitude of deformation
    • Streamlines: Shows direction of principal stress
    • Hyperstreamlines: Combines both with weighted influence
    • Ellipses: Represents the tensor as oriented ellipses
    Tensor Field Implementation

    Curl Noise Mathematics

    Curl noise generates divergence-free vector fields, perfect for fluid-like motions. It\'s based on the curl operator from vector calculus:

    ∇ × Ψ = (∂Ψz/∂y - ∂Ψy/∂z, ∂Ψx/∂z - ∂Ψz/∂x, ∂Ψy/∂x - ∂Ψx/∂y)

    In 2D, this simplifies to a scalar field where the curl is perpendicular to the plane:

    curl(Ψ)(x,y) = ∂Ψy/∂x - ∂Ψx/∂y

    This mathematical property ensures the resulting vector field has zero divergence (∇·v = 0), creating perfect flow patterns without sources or sinks.

    Curl Noise Implementation

    Domain Warp Mathematics

    Domain warping is a technique where the input coordinates (the domain) of a noise function are displaced or distorted by another noise function. This creates swirling, turbulent, or flowing effects in the final pattern. Instead of sampling noise at point P, we sample at P + offset(P), where offset(P) is itself a noise function.

    Noisefinal(P) = Noisebase(P + Swarp × Noisedisplacement(P × Fwarp))

    Where:

    • P: The input coordinate (e.g., (x,y)).
    • Noisebase: The primary noise function (e.g., Perlin, Simplex).
    • Noisedisplacement: A noise function generating the offset vectors.
    • Swarp: Warp strength, controlling the magnitude of distortion.
    • Fwarp: Warp frequency, controlling the detail of the distortion.

    By applying this recursively (warping the domain of the displacement noise itself), more complex fractal warping effects can be achieved.

    Domain Warp Implementation

    Fractal Noise (FBM) Mathematics

    Fractal noise, often implemented as Fractal Brownian Motion (FBM), is a fundamental technique for generating natural-looking textures. It is constructed by summing multiple layers (octaves) of a base noise function (like Perlin or Simplex). Each successive octave has a higher frequency and a lower amplitude.

    FBM(P) = ∑N-1i=0 Ai × Noise(Fi × P)

    Where:

    • P: The input coordinate.
    • N: The number of octaves (layers of detail).
    • Noise: The base coherent noise function (e.g., Perlin).
    • A: Amplitude factor (persistence, typically < 1, e.g., 0.5). Controls how much amplitude decreases per octave.
    • F: Frequency factor (lacunarity, typically > 1, e.g., 2.0). Controls how much frequency increases per octave.

    The summation creates a pattern that exhibits self-similarity across different scales, characteristic of many natural phenomena.

    Fractal Noise (FBM) Implementation

    Waves Noise Mathematics

    Waves noise is typically generated by summing multiple sine or cosine wave functions. Each wave can have its own amplitude, frequency, phase, and direction. The superposition of these waves can create a wide variety_of_patterns, from simple ripples to complex interference effects.

    Waves(P) = ∑i Ai × sin(ki ⋅ P + ωit + φi)

    Where for each wave i:

    • P: The input coordinate (e.g., (x,y)).
    • Ai: Amplitude of the wave.
    • ki: Wave vector (determines direction and spatial frequency/wavelength). Its magnitude |ki| = 2π / λi.
    • ωi: Angular frequency (determines temporal oscillation speed).
    • t: Time.
    • φi: Phase offset.
    • ⋅: Dot product.

    Simpler forms might fix directions or use scalar frequencies if directionality is not complex.

    Waves Noise Implementation

    Gaussian Noise Mathematics

    Gaussian noise is a statistical noise characterized by a probability density function (PDF) that follows the Gaussian (or normal) distribution. Its values are typically clustered around a mean (μ), with a spread determined by the standard deviation (σ). In image processing, it's often used to simulate random sensor noise or as a basis for other effects.

    PDF: f(x | μ, σ2) = (1 / (σ√(2π))) × e-((x-μ)2 / (2σ2))

    For generating Gaussian noise, random numbers are drawn from this distribution. In practice, libraries provide functions to directly generate such noise (e.g., torch.randn which samples from N(0,1)).

    • μ (Mean): The average value, often 0 for noise.
    • σ (Standard Deviation): Controls the "spread" or intensity of the noise. σ2 is the variance.

    Gaussian noise is "white" if its values are statistically independent and identically distributed at each point.

    Gaussian Noise Implementation

    Heterogeneous FBM Mathematics

    Heterogeneous Fractal Brownian Motion (Hetero FBM) is an extension of standard FBM. While standard FBM uses constant parameters (like persistence/amplitude falloff and lacunarity/frequency gain) across all octaves and spatial locations, Hetero FBM allows these parameters, or the base noise characteristics, to vary spatially. This creates textures with non-uniform complexity, where some areas might be smoother and others rougher or more detailed.

    HeteroFBM(P) = ∑N-1i=0 A(P, i) × Noise(F(P, i) × P, params(P,i))

    Where:

    • P: The input coordinate.
    • N: The number of octaves.
    • A(P, i): Spatially varying amplitude for octave i at point P.
    • F(P, i): Spatially varying frequency for octave i at point P.
    • Noise(..., params(P,i)): Base noise function whose own internal parameters might also vary spatially.

    The spatial variation itself is often controlled by another noise function or a predefined map. For example, the Hurst exponent (H), which relates to persistence, could be made to vary across the domain.

    Heterogeneous FBM Implementation

    Interference Noise Mathematics

    Interference patterns arise from the superposition of two or more waves. When waves meet, they can reinforce each other (constructive interference) or cancel each other out (destructive interference), depending on their relative phases and amplitudes. This principle is fundamental in physics (e.g., light and sound waves) and can be used to generate complex visual patterns.

    Pattern(P) = f(Wave1(P), Wave2(P), ..., WaveN(P))

    A common way to generate interference is by summing or multiplying wave functions:

    Example with two sine waves:

    V(P) = A1sin(k1⋅P + φ1) + A2sin(k2⋅P + φ2)

    Or using noise functions as sources:

    V(P) = Noise1(P) + Noise2(P × S + O)

    Where S is a scale and O is an offset for Noise2 to make it different from Noise1. The visual character depends heavily on the frequencies and relative phases of the interfering sources.

    Interference Noise Implementation

    Spectral Noise Mathematics

    Spectral noise generation involves directly defining or manipulating the noise's properties in the frequency domain (its spectrum) using techniques like the Fourier Transform. By controlling the amplitude and phase of different frequencies, a wide variety_of_textures can be created, from smooth to rough, or with specific directional biases.

    The general process is:

    1. Start with white noise in the spatial domain or directly create a spectrum.
    2. Compute its Fast Fourier Transform (FFT) to get the frequency domain representation (spectrum).
    3. Modify the spectrum: Apply a filter (e.g., 1/fβ for pink/brown/blue noise, band-pass, directional filters).
    4. Compute the Inverse Fast Fourier Transform (IFFT) to convert the modified spectrum back to the spatial domain.
    SpatialNoise = IFFT( Filter(Spectrum) × FFT(InitialNoise) )

    The filter often takes the form 1/fβ, where f is frequency and β controls the "color" of the noise (e.g., β=0 for white, β=1 for pink, β=2 for brown/red).

    Spectral Noise Implementation

    3D Projection Noise Mathematics

    3D Projection noise involves generating a 3D noise field (e.g., 3D Perlin, Simplex, or FBM) and then sampling a 2D slice from it. This technique creates 2D patterns that appear to have depth, volume, or temporal evolution if the slice position changes over time.

    Noise2D(x, y) = Noise3D(x × Sxy, y × Sxy, zslice × Sz + T × Vt)

    Where:

    • (x, y): 2D coordinates for the output noise.
    • Noise3D: A 3D coherent noise function.
    • Sxy: Spatial scaling factor for x and y axes.
    • zslice: The depth or position of the 2D slice along the third dimension (often z).
    • Sz: Scaling factor for the z-dimension, controlling "thickness" or detail along z.
    • T: Time parameter for animation.
    • Vt: Velocity of slicing through the 3D noise field over time.

    By animating zslice or rotating the sampling plane, dynamic evolving textures can be created from a static 3D noise volume.

    3D Projection Noise Implementation

    🎭 Blend Modes ⚖️ The Alchemical Combinations

    ShaderNoiseKSampler offers various blend modes to combine shader noise with base noise, each creating distinct alchemical transformations:

    🧩 Blend Operations

    Normal

    Simple linear interpolation between base and shader noise, controlled by shader_strength

    result = base * (1.0 - α) + shader * α
    Add

    Adds shader noise to base noise, creating brightened areas where patterns align

    result = base + shader * α
    Multiply

    Multiplies base and shader noise, darkening the overall pattern

    result = base * (shader * α + (1.0 - α))
    Screen

    Inverts, multiplies, then inverts again, brightening patterns

    result = 1.0 - (1.0 - base) * (1.0 - shader * α)
    Overlay

    Combines Multiply and Screen modes for enhanced contrast

    result = base < 0.5 ? 2.0 * base * shader : 1.0 - 2.0 * (1.0 - base) * (1.0 - shader)
    Soft Light

    Darkens or lightens colors depending on shader noise

    result = (1.0 - base) * base * shader + base * (1.0 - (1.0 - base) * (1.0 - shader))
    Hard Light

    More intense version of Overlay with sharper contrast

    result = shader > 0.5 ? 1.0 - (1.0 - base) * (1.0 - 2.0 * (shader - 0.5)) : 2.0 * base * shader
    Difference

    Subtracts darker color from lighter color, creating distinctive edges

    result = base * (1.0 - α) + abs(base - shader) * α

    🧿 Blend Mode Harmonics

    The choice of blend mode significantly impacts the generated image. Different blend modes resonate with different shader noise types and prompt themes:

    • Multiply + Cellular Noise ⟶ Organic, tissue-like structures
    • Add + Curl Noise ⟶ Ethereal, flowing energy patterns
    • Overlay + Tensor Field ⟶ Crystalline, structured patterns
    • Soft Light + Domain Warp ⟶ Dream-like, fluid transformations
    • Difference + Spectral Noise ⟶ Boundary-focused, edge-highlighting patterns

    Experiment with these combinations to find the blend harmonics that resonate with your artistic vision.

    🌀 Noise Transformations 💎 Mathematical Metamorphosis

    Beyond the core shader noise patterns, ShaderNoiseKSampler offers additional mathematical transformations that can dramatically alter the character of the generated shader noise:

    🧮 Mathematical Operators

    Reverse

    Inverts the sign of the noise, turning peaks into valleys and vice versa

    T(noise) = -noise
    Inverse

    Reverses the values within the 0-1 range, preserving the overall pattern

    T(noise) = 1.0 - noise
    Absolute

    Takes the absolute value, creating sharp ridges at zero-crossings

    T(noise) = |noise|
    Square

    Squares the values, enhancing high values and diminishing low values

    T(noise) = noise2
    Square Root

    Takes the square root, enhancing low values and compressing high values

    T(noise) = √noise
    Log

    Takes the logarithm, greatly enhancing low values

    T(noise) = log(noise + ε)
    Sin

    Applies the sine function, creating oscillating patterns

    T(noise) = sin(noise × π)
    Cos

    Applies the cosine function, creating alternating bands

    T(noise) = cos(noise × π)

    🧪 Transformation Alchemy

    These transformations can dramatically alter the character of shader noise patterns, creating new structural properties:

    • Absolute transform creates ridge-like features along zero-crossings, perfect for geological formations
    • Square transform enhances high-intensity regions while subduing low-intensity areas, ideal for creating focused areas of interest
    • Sin/Cos transforms create banded patterns, excellent for stratified or layered structures
    • Log transform magnifies subtle details in low-intensity regions, revealing hidden structures

    These transformations can be combined with different shader noise types and blend modes to create an almost infinite variety of structural guidance patterns.

    🎬 Temporal Coherence ⏳ Evolving Noise for Animations

    ShaderNoiseKSampler's temporal coherence feature ensures consistent noise patterns. For sequences like video frames, it helps maintain frame-to-frame consistency by evolving noise naturally over time. For single image generations, it ensures the base noise is derived consistently from the main seed, leading to predictable noise structures when parameters are tweaked.

    ⚙️ Enabling Evolving Animations

    Temporal coherence fundamentally changes how shader noise is generated:

    • use_temporal_coherence (Boolean): This is the primary toggle. When enabled, ShaderNoiseKSampler ensures noise is generated consistently. For videos, it uses techniques (like the TemporalCoherentNoiseGenerator) that treat time as an evolving dimension. For images, it ensures the same base noise is used if the main seed is unchanged.
    • Consistent Base Seed: For temporal coherence to be effective (for both images and videos), the main seed input must remain constant. For videos, variation then comes from the noise evolving over an internal 'time' parameter. For images, this ensures that if other shader parameters are changed, the underlying base noise structure remains the same.
    • Consistent/Evolving Patterns: For videos, this mode generates patterns that transform coherently from one frame to the next, leading to more stable animations. For single images, it means the generated noise pattern will be the same for a given seed, even if other parameters (like shader strength or blend mode) are changed, allowing for more predictable exploration.

    Conceptual animation preview placeholder.

    Conceptual Temporal Noise (Python)

    🎭 Shape Masks 💠 Geometric Modulation

    Shape masks provide spatial control by modulating shader noise patterns with procedurally generated geometric overlays. These masks, typically grayscale tensors (0.0 for no effect, 1.0 for full effect), guide the generative process, influencing compositions and textural details.

    Mask Generation & Application

    Shape masks are procedurally generated within the node as fixed grayscale patterns where values range from 0.0 (no effect area) to 1.0 (full effect area). The generated mask \( M \) is then used to modulate an input field (e.g., a noise or velocity field \( V \)) based on the shape_mask_strength \( \\alpha_s \). The application is a linear interpolation:

    V_{masked} = V \\times (1 - \\alpha_s) + (V \\times M) \\times \\alpha_s

    This means if \( \\alpha_s = 1 \), the field is fully multiplied by the mask pattern (\( V \\times M \)). If \( \\alpha_s = 0 \), the mask has no effect. Intermediate values blend between the original and the mask-modulated field. Key characteristics include:

    • Mask Type: Defines the base geometry from a selection including Radial, Linear, and various Geometric or Procedural patterns.
    • Mask Strength: The \( \\alpha_s \) parameter, controlling the blend intensity of the mask\'s effect.
    • Edge Handling: Mask types inherently produce different edge styles, some with soft, feathered falloffs (often using smoothstep internally) and others with hard, thresholded edges.

    Note that while masks might appear animated in visualization previews, in the backend implementation they are static patterns applied at generation time. Any animation effects in the final output would come from changing parameters between frames.

    Radial
    Linear
    Grid
    Vignette
    Spiral
    Hexgrid
    Wavy
    Concentric
    Illustrative Shape Mask Application (Python)

    🎨 Creative Applications

    The interplay between different mask types, the noise they modulate, and various parameter settings (like shape_mask_strength, noise_scale, etc.) allows for a wide range of artistic effects. Consider these combinations:

    • Vignettes & Focus: Soft Radial or Vignette masks to draw attention centrally.
    • Structured Organics: Grid Masks combined with Cellular Noise for bio-mechanical effects.
    • Atmospheric Depth: Linear Gradient Masks to simulate fog or distance with flowing noise.
    • Selective Detailing: Geometric Masks to apply high-frequency noise to specific regions.

    Experiment with mask types and their strengths in conjunction with noise parameters to guide the generative process.

    🌈 Color Schemes ⚙️ Chromatic Adjustments

    ShaderNoiseKSampler allows for the application of color transformations directly to the generated shader noise patterns. It's important to understand that this is not a traditional post-processing color adjustment. Instead, the color information is integrated into the shader noise before it influences the diffusion model during the sampling process. Consequently, these 'colors' can function as an integral part of the shader noise's texture and structure, subtly guiding the model's interpretation and shaping the features, patterns, and overall aesthetic of the final image. This technique offers a unique way to enhance visual impact and steer the generative outcome.

    Color Transformation Process

    The color mapping function transforms a normalized noise value t ∈ [0,1] into RGB color space through gradient functions. Each color scheme defines specific transfer functions for each channel, creating distinctive visual characteristics that can alter how the diffusion model interprets the noise structure.

    C(t) = (R(t), G(t), B(t))

    Key characteristics of the coloring process include:

    • Color Scheme Selection: Different schemes provide various perceptual encodings of the noise data.
    • Color Intensity: Controls how strongly the coloring affects the final noise blend, typically between 0.0 (no effect) and 1.0 (full effect).
    • Channel Blending: The colored information is blended into the noise channels before they influence the diffusion process.
    The choice of color scheme significantly impacts how shader noise patterns guide the generative process. Different schemes create various perceptual encodings that can emphasize different aspects of the shader noise structure, potentially influencing the final image's mood, texture, and compositional elements.
    Spectral Schemes - Emphasizing Transitions
    Inferno
    Magma
    Plasma
    Viridis
    Technical Schemes - Enhancing Perception
    Turbo
    Jet
    Parula
    Rainbow
    Hot

    Applying Color Schemes

    ShaderNoiseKSampler allows for the selection of various color schemes that are applied to the generated noise. The intensity of this coloration can also be controlled, determining how strongly the chosen color scheme influences the noise that guides the diffusion model.

    Color Scheme Effects on Generation

    • Contrast Enhancement: Color schemes like Inferno and Jet can increase the perceived contrast in the noise pattern, potentially leading to more defined boundaries in the generated image.
    • Perceptual Organization: Schemes like Viridis and Turbo help to organize the noise data perceptually, which can influence how the model interprets spatial relationships.
    • Mood and Tone: The predominant hues of a color scheme can subtly influence the mood or tonal quality of the generated image, even when the colors themselves aren't directly visible.
    • Detail Emphasis: Some schemes are better at emphasizing small details within the noise pattern, which can lead to enhanced textural complexity in the final output.
    Color Scheme Implementation

    🛠️ Usage 🔧 Working with this Tool

    ShaderNoiseKSampler integrates advanced noise generation directly into the ComfyUI sampling process. It replaces the standard KSampler's noise generation with a sophisticated system that allows for multi-stage shader application, diverse noise types, transformations, and blending, offering fine-grained control over the creative output.

    Core Workflow Integration

    1. Connect Inputs:
      • model: The primary AI model for generation.
      • positive & negative: Conditioning prompts.
      • latent_image: The input latent to be processed.
    2. Basic Sampling Parameters:
      • seed: For reproducibility use a fixed seed value for consistent results.
      • steps: Number of sampling iterations.
      • cfg: Classifier-Free Guidance scale.
      • sampler_name & scheduler: Standard KSampler choices.
      • denoise: Controls the extent of denoising.
    3. Shader Noise Configuration: This is where ShaderNoiseKSampler shines:
      • sequential_stages: Number of shader stages applied sequentially over defined step ranges.
      • injection_stages: Number of shader stages applied at specific injection points during sampling.
      • shader_strength: Global strength of the shader noise influence. Set to 0.0 to use only base noise.
      • blend_mode: How shader noise combines with base noise (e.g., multiply, add, overlay).
      • noise_transform: Mathematical operations applied to the generated noise (e.g., absolute, sin, sqrt).
      • use_temporal_coherence: For generating frame-consistent noise, useful in animations.
    4. (Optional) Advanced Control:
      • custom_sigmas: Provide a custom sigma schedule to override the model's default.
    5. Run Workflow: Execute the ComfyUI graph. The ShaderNoiseKSampler will dynamically generate and blend noise according to your settings throughout the sampling process.

    ⚙️ Key Parameter Explanations

    Sequential vs. Injection Stages

    sequential_stages apply shader noise over segments of the diffusion process. The total steps are divided among these stages. injection_stages apply shader noise at specific, discrete steps. Sequential stages don't seem to add time to the generation but injection stages will add some time to the generation.

    Shader Strength & Distributions

    shader_strength is a global multiplier. Higher shader strength can lead to increased artifacting or generational oddities, but not always and not with all models or shader noise types. Small variations in shader strength is a good way to maintain the sum concepts of an image and not heavily vary its degree of difference.

    Noise Transformation

    The noise_transform (e.g., absolute, sin, sqrt) is a global setting applied to the generated shader noise within each stage before it's blended with the base noise. The chosen transformation is used consistently across all stages.

    Temporal Coherence

    When use_temporal_coherence is enabled, the node aims to generate consistent noise. For videos, this means noise evolves smoothly over a "time" dimension (often using 3D/4D noise with one dimension as time), with the base seed consistent across frames. For images, it ensures the noise pattern is consistently derived from the main seed, even with other parameter changes.

    Custom Sigmas

    custom_sigmas allows advanced users to define their own noise schedule (sigmas), giving more control over how noise is added and removed at each step. This can significantly alter the sampling dynamics.

    Blend Mode

    The blend_mode (e.g., multiply, add, overlay) is a global setting that determines how the shader noise combines with the base noise. This selected blend mode is used for all stages.

    🧠 Creative Techniques & Advanced Usage

    • Shader Visualizer as a Guide: Using the shader visualizer in connection to the generation process is crucial for gaining insights into your parameter adjustments. The shader display was designed as a visual aid, not just for presentation. It helps you understand the shader noise\'s impact on the output image and serves as a navigational tool for latent space, mirroring backend shader parameters.
    • Detail through Noise Scale & Octaves: Higher noise scale and octaves can contribute to more detailed images. Essentially, a more "noisy" input increases the likelihood of observing finer-grained detail in the output.
    • Parameter Interdependence: Parameters are not always independent. If phase shift adjustments seem insufficient, try incorporating a bit of warp strength. Conversely, if your noise scale and octaves are already high, parameters like warp strength and phase shift might appear less impactful.
    • Color Intensity with Shape Masks: Color intensity is only effective when a shape mask is active. Once active, it can be used to introduce subtle to significant variations in your image, depending on the input values.
    • Shape Mask Strength: Similar to color intensity, shape mask strength is only usable when a shape mask is active. When enabled, it allows for small to large image variations based on your input values.
    • Warp & Phase with Active Masks/Colors: You might notice that when a color scheme or a shape mask (especially with higher strength/intensity) is active, the perceptual impact of warp_strength and phase_shift can seem diminished. This is likely because the structural influence of the mask or the channel modifications from coloring are significantly shaping the noise, potentially overshadowing the more subtle distortions or shifts from warp and phase. Experiment with moderating mask/color influence if you need to bring out the warp/phase effects more clearly.
    • Quick Ideation: For rapid idea generation without the need to constantly save parameters, consider using the (direct) version of this node. As is rapid parameter changes cannot be made to queue in succession, due to the way the node is currently implemented.
    • Saving Parameters: The "Save Shader Parameters" button (found on the ShaderNoiseKSampler node) allows you to save your current shader settings. These are saved to custom_nodes/ComfyUI-ShaderNoiseKsampler/data/shader_params.json. Note: this file must be named exactly shader_params.json in this specific directory and will be overwritten on each save. This is crucial for features that load these parameters. Use the (direct) version of this node to avoid saving parameters.
    • Shader Noise Palettes & Performance: Each Shader Noise Type (or palette) offers a distinct lens through which to explore the latent space, revealing different facets and possibilities within a seed. Most shader noise types process at roughly comparable speeds, typically adding only about 3-4 seconds to the generation time compared to a standard KSampler. While some may occasionally take a moment longer to initialize or load, the overall impact on generation time is generally minimal.

    🌅 Conclusion ☯ The Path of Creation

    ShaderNoiseKSampler is a bridge between the mathematical sublime and practical artistry. By understanding and harnessing the sacred geometry of shader noise, artists can guide the generative process with precision and intentionality, transforming pure mathematical concepts into tools of creative expression.

    It is important to note that this project represents a concept in its infancy—a minimal viable product of ongoing research. Much remains to be explored and understood in this fascinating intersection of mathematical noise patterns and generative art. The techniques and approaches presented here are foundational stepping stones in a journey that has only just begun.

    This work constitutes original, independent investigation and implementation—a product of genuine curiosity and hands-on experimentation rather than an adaptation of existing research. The path forward relies on community support, both in collaborative exploration and financial backing, to fuel further innovation. By supporting grassroots research like this, you contribute directly to the advancement of creative technology driven by unfiltered exploration and authentic discovery.

    May your journey with ShaderNoiseKSampler be one of discovery, inspiration, and creation—of digital tapestries woven from the very fabric of mathematical reality.

    "In every shader noise pattern lies a universe of possibility
    In every parameter, a doorway to creation"
    - The Shader Matrix
    `; // This line should end the template literal correctly content.innerHTML = treatiseHTML; // Attach listener to the close button *inside* the treatiseHTML const closeButtonInTreatise = content.querySelector('.close-button'); if (closeButtonInTreatise) { closeButtonInTreatise.onclick = (e) => { e.stopPropagation(); closeModalCleanup(); }; } modal.appendChild(content); document.body.appendChild(modal); // Accessibility: Focus Trap & Initial Focus const focusableSelectors = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; modal.addEventListener('keydown', (e) => { if (e.key === 'Tab') { const focusableElements = Array.from(modal.querySelectorAll(focusableSelectors)) .filter((el) => el.offsetParent !== null && !el.hasAttribute('disabled')); if (focusableElements.length === 0) return; const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; if (e.shiftKey) { if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } else { if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } } }); // Accessibility: Set focus to the close button when modal opens // Using a small timeout to ensure DOM insertion is complete and to play nice with screen readers setTimeout(() => { const closeBtn = content.querySelector('.close-button'); if (closeBtn) { closeBtn.focus(); } }, 50); // Call the renderer for noise visualizations if (window.NoiseVisualizer?.renderAllInModal) { setTimeout(() => { window.NoiseVisualizer.renderAllInModal(content); }, 0); } else { console.warn("NoiseVisualizer not found. Ensure noise_visualizer.js is loaded and available on the window object."); } // Initial scroll to top of modal content content.scrollTop = 0; // Activate the scroll-to-top button functionality window.setupScrollTop(content); // Pass the 'content' element }); // Add tooltip to the button button.tooltip = "Show Shader Matrix & Documentation (Alt+M)"; // Position the button appropriately based on node type (from matrix_button - Copy.js) if (nodeData.name === "ShaderNoiseKsampler") { // For KSampler node, add to a specific section or position if (!button.options) { button.options = {}; } button.options.section = "advanced"; // Custom button style for the KSampler node button.label = "📊 Show Shader Matrix"; } else { // Default styling for ShaderDisplay (label is already "Show Matrix") // button.label = "Show Matrix"; // No change needed if initialized with "Show Matrix" } // Set button appearance (from matrix_button - Copy.js) button.name = "📊 Show Shader Matrix"; button.serialize = false; // Don't include in serialization // For ShaderDisplay, the button will use default positioning. // For ShaderNoiseKsampler, without a section, it should append after other widgets. }; // Call addMatrixButton conditionally (from matrix_button - Copy.js) if (self.constructor.type_name === "ShaderNoiseKsampler") { addMatrixButton(); } else { // For other nodes, add after a small delay to ensure all widgets are ready setTimeout(addMatrixButton, 50); } const triggerMatrixButton = () => { const matrixButtonWidget = self.widgets.find((w) => w.name === "📊 Show Shader Matrix" && w.type === "button"); if (matrixButtonWidget && typeof matrixButtonWidget.callback === 'function') { matrixButtonWidget.callback.call(matrixButtonWidget.value, app.canvas, self, null, null); } else { console.warn("Matrix button widget not found or callback is not a function for Alt+M."); } }; const handleMatrixKeyDown = (event) => { if (event.altKey && event.key.toLowerCase() === 'm') { const appCanvas = app.canvas; if (appCanvas && (appCanvas.current_node === self || (appCanvas.selected_nodes && appCanvas.selected_nodes[self.id]))) { const activeEl = document.activeElement; if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable)) { return; } console.log("Alt+M detected for current/selected node to show matrix."); event.preventDefault(); event.stopPropagation(); triggerMatrixButton(); } } }; document.addEventListener('keydown', handleMatrixKeyDown); self.handleMatrixButtonKeyDown = handleMatrixKeyDown; // Store for removal const originalOnRemoved = self.onRemoved; self.onRemoved = function () { if (self.handleMatrixButtonKeyDown) { document.removeEventListener('keydown', self.handleMatrixButtonKeyDown); delete self.handleMatrixButtonKeyDown; console.log("Removed Alt+M keydown listener for matrix button on node:", self.id); } if (originalOnRemoved) { originalOnRemoved.apply(self, arguments); } }; // --- End Keybinding Logic --- }; } }); })(); //# sourceMappingURL=matrix_button.js.map