1 scriptable-empty-latent
Balazs Horvath edited this page 2026-04-18 10:29:32 +02:00

ScriptableEmptyLatent Node

Overview

The ScriptableEmptyLatent node allows you to write Python scripts that generate empty latents for diffusion models. It is nearly identical to ScriptableLatent, with the same purpose and functionality. The distinction is mostly organizational - ScriptableEmptyLatent is specifically for creating empty (zero or near-zero) latents as a starting point for generation.

What is an Empty Latent?

An empty latent is a latent representation filled with zeros (or small random values). This serves as a blank canvas for the diffusion process. When you start with an empty latent and apply the diffusion reverse process, the model generates an image from pure noise.

Think of it like a blank piece of paper:

  • Empty latent = blank paper
  • Diffusion process = drawing on the paper
  • Final result = a complete image

Inputs

Parameter Type Default Range Description
width INT 512 64-8192 Width of the latent in pixels
height INT 512 64-8192 Height of the latent in pixels
batch_size INT 1 1-64 Number of latents to generate (for batching)
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

  • latent: A dictionary with key "samples" containing a tensor of shape [batch_size, 4, height, width] with dtype float32

Default Script

# Scriptable Empty Latent: set 'output_latent' (dict with 'samples' tensor [B,C,H,W]).
# Injected: width, height, batch_size, seed, torch, np.
# Example - empty latent:
#   output_latent = {'samples': torch.zeros((batch_size, 4, height, width), dtype=torch.float32)}

output_latent = {'samples': torch.zeros((batch_size, 4, height, width), dtype=torch.float32)}

How the Default Script Works

output_latent = {'samples': torch.zeros((batch_size, 4, height, width), dtype=torch.float32)}
  • torch.zeros(): Creates a tensor filled with zeros
  • (batch_size, 4, height, width): The shape of the tensor
    • batch_size: Number of latents in the batch
    • 4: Number of channels (standard for stable diffusion)
    • height: Height of the latent
    • width: Width of the latent
  • dtype=torch.float32: Uses 32-bit floating point numbers
  • {'samples': ...}: Wraps the tensor in a dictionary with the key "samples" (ComfyUI's expected format)

This creates a completely empty (zero) latent.

Available Variables

Your script has access to these variables:

  • width (int): The width parameter
  • height (int): The height parameter
  • batch_size (int): The batch_size parameter
  • seed (int): The seed parameter
  • torch: The PyTorch library
  • np: The NumPy library

If enable_io_helpers is True, you also get:

  • get_output_dir(): Returns ComfyUI's output directory path
  • get_input_dir(): Returns ComfyUI's input directory path
  • get_temp_dir(): Returns ComfyUI's temporary directory path

Example: Empty Latent (Default)

output_latent = {'samples': torch.zeros((batch_size, 4, height, width), dtype=torch.float32)}

This is the default behavior - creates a completely zero latent.

Example: Small Random Noise Latent

import torch

# Create a latent with very small random noise
torch.manual_seed(seed)
noise = torch.randn(batch_size, 4, height, width) * 0.01
output_latent = {'samples': noise}

Explanation:

  • torch.randn(): Generates random numbers from normal distribution
  • * 0.01: Scales to very small values (standard deviation 0.01)
  • This creates a nearly-empty latent with tiny variations
  • Sometimes used instead of pure zeros for numerical stability

Example: Latent with Specific Pattern

import torch

# Create a latent with a specific pattern (e.g., gradient)
x_coords = torch.linspace(-1, 1, width).view(1, 1, 1, width)
gradient = x_coords.expand(batch_size, 4, height, width)
output_latent = {'samples': gradient}

Explanation:

  • Creates a horizontal gradient in the latent
  • Values range from -1 to 1
  • This can influence the generation process
  • The diffusion model will start from this pattern instead of pure noise

Example: Conditional Empty Latent

import torch

# Create different empty latents based on seed
torch.manual_seed(seed)

# Use seed to determine a simple pattern
pattern_type = seed % 3

if pattern_type == 0:
    # Pure zero
    latent = torch.zeros(batch_size, 4, height, width)
elif pattern_type == 1:
    # Small noise
    latent = torch.randn(batch_size, 4, height, width) * 0.01
else:
    # Gradient
    x_coords = torch.linspace(-1, 1, width).view(1, 1, 1, width)
    latent = x_coords.expand(batch_size, 4, height, width)

output_latent = {'samples': latent}

Explanation:

  • Uses seed to determine which pattern to create
  • Three different patterns based on seed modulo 3
  • This allows for variation while still being deterministic

Example: Batch with Different Patterns

import torch

# Create a batch where each item has a different pattern
batch_latents = []

for i in range(batch_size):
    # Each batch item gets a different pattern based on its index
    pattern_type = i % 3
    
    if pattern_type == 0:
        latent = torch.zeros(4, height, width)
    elif pattern_type == 1:
        latent = torch.randn(4, height, width) * 0.01
    else:
        x_coords = torch.linspace(-1, 1, width).view(1, 1, width)
        latent = x_coords.expand(4, height, width)
    
    batch_latents.append(latent)

# Stack into batch tensor
output_latent = {'samples': torch.stack(batch_latents, dim=0)}

Explanation:

  • Creates different patterns for each batch item
  • Cycles through three patterns based on index
  • Result: batch with varied starting conditions

Comparison with ScriptableLatent

ScriptableEmptyLatent and ScriptableLatent are nearly identical:

Similarities:

  • Same inputs (width, height, batch_size, seed, script)
  • Same output format (latent dictionary)
  • Same available variables (torch, np, etc.)
  • Same error handling

Differences:

  • ScriptableEmptyLatent is specifically for empty/near-empty latents
  • ScriptableLatent can be used for any latent generation
  • The distinction is organizational/conventional

In practice, you can use either for the same purpose. ScriptableEmptyLatent exists to make workflows clearer when you're specifically creating empty latents.

Common Patterns

Pattern 1: Pure Zero Latent

output_latent = {'samples': torch.zeros((batch_size, 4, height, width), dtype=torch.float32)}

The most common use case - a completely blank starting point.

Pattern 2: Reproducible Random Latent

import torch

torch.manual_seed(seed)
noise = torch.randn(batch_size, 4, height, width) * 0.02
output_latent = {'samples': noise}

Reproducible small noise for consistent starting conditions.

Pattern 3: Structured Starting Point

import torch

# Create a radial gradient as starting point
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')
distance = torch.sqrt(X**2 + Y**2)
gradient = 1 - distance
gradient = torch.clamp(gradient, 0, 1)

# Expand to latent shape
latent = gradient.unsqueeze(0).unsqueeze(0).expand(batch_size, 4, height, width)
output_latent = {'samples': latent}

Creates a structured starting point that can influence generation.

Error Handling

The node includes error handling:

  • If the script fails to execute, it returns a zero latent
  • If the script doesn't set output_latent, it returns a zero latent
  • If the output format is invalid, it returns a zero latent

This ensures your workflow won't crash if there's a script error.

When to Use ScriptableEmptyLatent vs ScriptableLatent

Use ScriptableEmptyLatent when:

  • You want to create empty/near-empty latents
  • You want to make your workflow intent clear
  • You're starting generation from scratch

Use ScriptableLatent when:

  • You want to generate arbitrary latents
  • You might create non-empty latents
  • You want more flexibility

In practice, both nodes can do the same thing. The choice is about workflow clarity and organization.

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.

  • ScriptableLatent: Nearly identical node for general latent generation
  • Empty Latent Image: ComfyUI's built-in empty latent node
  • KSampler: Sampler that uses latents for generation