Table of Contents
- ScriptableMask Node
- Overview
- Mask Format in ComfyUI
- What is a Mask?
- Inputs
- Outputs
- Default Script
- How the Default Script Works
- Available Variables
- Example: Horizontal Gradient Mask
- Example: Vertical Gradient Mask
- Example: Radial Gradient Mask
- Example: Circular Mask
- Example: Soft Circular Mask (Gaussian)
- Example: Checkerboard Mask
- Example: Diagonal Gradient Mask
- Example: Random Noise Mask
- Example: Perlin-like Noise Mask
- Example: Vignette Mask
- Example: Batch of Masks
- Common Patterns
- Error Handling
- Security Considerations
- Related Nodes
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
ScriptableMask Node
Overview
The ScriptableMask node allows you to write Python scripts that generate masks. A mask is a grayscale image where each pixel value represents opacity or weight. In ComfyUI, masks are commonly used to:
- Control where an effect is applied (e.g., only apply color correction to certain areas)
- Blend between two images
- Control the strength of an effect at different locations
- Define regions for inpainting
Mask Format in ComfyUI
ComfyUI uses a specific mask format: a PyTorch tensor with shape [batch, height, width] or [height, width] where:
batch: Number of masks (optional, can be omitted for single mask)height: Mask height in pixelswidth: Mask width in pixels
Values are floating point numbers between 0.0 and 1.0, where:
- 0.0 = fully transparent (effect not applied)
- 1.0 = fully opaque (effect fully applied)
- Values between 0 and 1 = partial opacity/weight
What is a Mask?
Think of a mask like a stencil or a transparency sheet:
- Where the mask is white (value 1.0), the effect shows through completely
- Where the mask is black (value 0.0), the effect is blocked
- Gray areas (values between 0 and 1) partially apply the effect
Masks are grayscale because they only need to represent one value per pixel: the opacity or weight.
Inputs
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
| width | INT | 512 | 64-8192 | Width of the mask in pixels |
| height | INT | 512 | 64-8192 | Height of the mask in pixels |
| seed | INT | 0 | 0-4294967295 | Random seed for reproducibility |
| enable_io_helpers | BOOLEAN | False | - | Enable file I/O helper functions |
| script | STRING | (see below) | - | Python script to execute |
Outputs
- mask: A tensor of shape
[batch, height, width]or[height, width]with dtypefloat32, values in range [0, 1]
Default Script
# Scriptable Mask: set 'output_mask' (torch.Tensor [H,W] or [B,H,W] float 0-1).
# Injected: width, height, seed, torch, np.
# Example - white mask:
# output_mask = torch.ones((height, width), dtype=torch.float32)
# Example - gradient mask:
# output_mask = torch.linspace(0, 1, width).unsqueeze(0).repeat(height, 1)
output_mask = torch.ones((height, width), dtype=torch.float32)
How the Default Script Works
output_mask = torch.ones((height, width), dtype=torch.float32)
torch.ones(): Creates a tensor filled with ones(height, width): The shape (no batch dimension)dtype=torch.float32: Uses 32-bit floating point
This creates a completely white mask, meaning the effect will be applied everywhere at full strength.
Available Variables
Your script has access to:
width(int): The width parameterheight(int): The height parameterseed(int): The seed parametertorch: The PyTorch librarynp: The NumPy library
If enable_io_helpers is True, you also get:
get_output_dir(): Returns ComfyUI's output directory pathget_input_dir(): Returns ComfyUI's input directory pathget_temp_dir(): Returns ComfyUI's temporary directory path
Example: Horizontal Gradient Mask
import torch
# Create a gradient from left (0) to right (1)
gradient = torch.linspace(0, 1, width)
output_mask = gradient.unsqueeze(0).repeat(height, 1)
Explanation:
torch.linspace(0, 1, width): Creates values from 0 to 1 across the width.unsqueeze(0): Adds a dimension to make it[1, width].repeat(height, 1): Repeats the rowheighttimes to make[height, width]- Result: left side is black (0), right side is white (1), smooth gradient in between
Example: Vertical Gradient Mask
import torch
# Create a gradient from top (0) to bottom (1)
gradient = torch.linspace(0, 1, height)
output_mask = gradient.unsqueeze(1).repeat(1, width)
Explanation:
torch.linspace(0, 1, height): Creates values from 0 to 1 across the height.unsqueeze(1): Adds a dimension to make it[height, 1].repeat(1, width): Repeats the columnwidthtimes to make[height, width]- Result: top is black (0), bottom is white (1), smooth gradient in between
Example: Radial Gradient Mask
import torch
# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')
# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)
# Create gradient (1 at center, 0 at edges)
gradient = 1 - distance
gradient = torch.clamp(gradient, 0, 1) # Ensure values stay in [0, 1]
output_mask = gradient
Explanation:
- Coordinates range from -1 to 1 in both directions
sqrt(X**2 + Y**2): Euclidean distance from center (0, 0)1 - distance: Inverts so center is 1, edges are ~0.41torch.clamp(gradient, 0, 1): Ensures no negative values- Result: white center, fading to black at corners
Mathematical Background: The distance formula comes from the Pythagorean theorem:
d = sqrt(x² + y²)
For a unit square where x and y range from -1 to 1:
- Center (0, 0): d = 0 → gradient = 1 (white)
- Corner (1, 1): d = sqrt(2) ≈ 1.41 → gradient = -0.41 → clamped to 0 (black)
- Edge (1, 0): d = 1 → gradient = 0 (black)
Example: Circular Mask
import torch
# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')
# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)
# Create hard circle (1 inside radius, 0 outside)
radius = 0.7
circle = (distance < radius).float()
output_mask = circle
Explanation:
distance < radius: Creates a boolean mask (True inside circle, False outside).float(): Converts boolean to float (True → 1.0, False → 0.0)- Result: sharp circle with white inside, black outside
Example: Soft Circular Mask (Gaussian)
import torch
# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')
# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)
# Create soft circle using Gaussian
radius = 0.7
softness = 0.2
gaussian = torch.exp(-distance**2 / (2 * softness**2))
output_mask = gaussian
Explanation:
- Uses the Gaussian function: exp(-x² / (2σ²))
softness(σ) controls how soft the edge is- Result: smooth transition from white center to black edges
Mathematical Background: Gaussian Function
The Gaussian function is:
f(x) = exp(-x² / (2σ²))
Where:
xis the distance from centerσ(sigma) is the standard deviation, controlling edge softness
Key properties:
- Maximum value is 1 at x=0 (center)
- Approaches 0 as x increases
- Larger σ = softer, more gradual transition
- Smaller σ = sharper, more abrupt transition
Example: Checkerboard Mask
import torch
# Create checkerboard pattern
tile_size = 32
x_indices = torch.arange(width) // tile_size
y_indices = torch.arange(height) // tile_size
checkerboard = (x_indices.unsqueeze(0) + y_indices.unsqueeze(1)) % 2
output_mask = checkerboard.float()
Explanation:
torch.arange(width) // tile_size: Divides width into tiles- Adding the indices creates alternating sums
% 2: Modulo 2 gives alternating 0 and 1- Result: alternating black and white squares
Example: Diagonal Gradient Mask
import torch
# Create diagonal gradient from top-left to bottom-right
y_coords = torch.linspace(0, 1, height)
x_coords = torch.linspace(0, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')
# Average of x and y coordinates
diagonal = (X + Y) / 2
output_mask = diagonal
Explanation:
- Both coordinates range from 0 to 1
(X + Y) / 2: Average creates diagonal gradient- Result: top-left is black (0), bottom-right is white (1)
Example: Random Noise Mask
import torch
# Set seed for reproducibility
torch.manual_seed(seed)
# Create random noise
noise = torch.rand(height, width)
output_mask = noise
Explanation:
torch.rand(): Generates random values uniformly from [0, 1]torch.manual_seed(seed): Ensures same random values each time with same seed- Result: random speckled pattern
Example: Perlin-like Noise Mask
import torch
from scipy.ndimage import gaussian_filter
import numpy as np
# Set seed for reproducibility
np.random.seed(seed)
# Generate random noise
noise = np.random.rand(height, width)
# Apply Gaussian filter to smooth it
smooth_noise = gaussian_filter(noise, sigma=10)
# Convert to tensor
output_mask = torch.from_numpy(smooth_noise).float()
Explanation:
np.random.rand(): Generates random noisegaussian_filter(noise, sigma=10): Blurs the noisesigmacontrols blur amount- Larger sigma = smoother, larger features
- Smaller sigma = sharper, more detailed
- Result: smooth, organic-looking noise pattern
Mathematical Background: Gaussian Filter
The Gaussian filter blurs an image by convolving it with a Gaussian kernel. This is similar to averaging nearby pixels but with weights that decrease with distance.
The Gaussian kernel in 2D is:
G(x, y) = exp(-(x² + y²) / (2σ²))
Where σ controls the spread of the blur. This creates smooth, continuous transitions rather than sharp edges.
Example: Vignette Mask
import torch
# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')
# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)
# Create vignette (dark corners, bright center)
vignette = 1 - distance**2 # Squared for stronger falloff
vignette = torch.clamp(vignette, 0, 1)
output_mask = vignette
Explanation:
- Similar to radial gradient but uses squared distance
- Squaring makes the falloff more dramatic
- Result: bright center, rapidly darkening corners
Example: Batch of Masks
import torch
# Create multiple masks in a batch
batch_size = 4
masks = []
for i in range(batch_size):
# Each mask has a different gradient position
gradient = torch.linspace(0, 1, width)
shifted = torch.roll(gradient, shifts=i * width // batch_size, dims=0)
mask = shifted.unsqueeze(0).repeat(height, 1)
masks.append(mask)
# Stack into batch tensor
output_mask = torch.stack(masks, dim=0) # Shape: [4, height, width]
Explanation:
- Creates 4 masks with gradients at different positions
torch.roll(): Shifts the gradient circularly.stack(): Combines into a batch tensor- Result: 4 masks, each with gradient shifted differently
Common Patterns
Pattern 1: Threshold a Gradient
import torch
# Create gradient
gradient = torch.linspace(0, 1, width).unsqueeze(0).repeat(height, 1)
# Apply threshold (binary mask)
threshold = 0.5
binary = (gradient > threshold).float()
output_mask = binary
Pattern 2: Combine Multiple Masks
import torch
# Create two masks
mask1 = torch.ones((height, width), dtype=torch.float32) # All white
mask2 = torch.zeros((height, width), dtype=torch.float32) # All black
# Combine by multiplication (intersection)
combined = mask1 * mask2 # Result: all black
# Combine by addition (union)
combined = torch.clamp(mask1 + mask2, 0, 1) # Result: all white
# Blend by averaging
combined = (mask1 + mask2) / 2 # Result: 0.5 everywhere
Pattern 3: Use Conditional Logic
import torch
# Create coordinates
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')
# Create different masks based on conditions
mask = torch.where(
X > 0, # Condition
torch.ones_like(X), # Value if True (right side)
torch.zeros_like(X) # Value if False (left side)
)
output_mask = mask
Error Handling
The node includes error handling:
- Invalid output types are rejected
- Output is automatically clamped to [0, 1]
- Invalid shapes are corrected when possible
- Errors fall back to a white mask
Security Considerations
This node uses exec() to run your script. This is intentionally powerful for local development but should not be used in production or exposed to untrusted users.
Related Nodes
- ScriptableImage: For generating full-color images
- ScriptableLatent: For generating latents
- Mask-related nodes in ComfyUI: MaskToRegion, MaskComposite, etc.