Cinema 4D’s Field system is one of the most powerful tools for procedural animation, clone control, and dynamic effects. The Python script you’ve shared extends the flexibility of Fields by introducing custom fade, dissolve, and randomness behaviors that can be tailored to any MoGraph setup. Let’s break down what this script does, why it’s useful, and how you can apply it in your projects.

🎯 Purpose of the Script
This script is designed to control the strength of clones or field values over time with advanced options for fading, dissolving, and randomization. Instead of relying solely on Cinema 4D’s built-in linear fades, it allows you to:
- Define start frames and fade durations for precise timing.
- Apply easing functions (linear, quadratic, smooth, exponential, overshoot) for more natural transitions.
- Choose between uniform fades (all clones fade together) or random dissolves (clones turn on/off individually).
- Introduce quantization steps to create stepped transitions instead of smooth fades.
- Control randomness with seed values and optional position-based seeding for reproducibility.
⚡ Why It’s Useful
This script is particularly valuable because it gives motion designers fine-grained control over how clones appear, disappear, or change strength.
- Creative flexibility: You can achieve cinematic dissolves, glitchy stepped fades, or smooth organic transitions.
- Procedural control: Randomness can be locked to clone positions, ensuring consistent results across renders.
- Efficiency: Instead of manually animating hundreds of clones, you can automate fade/dissolve behaviors with a single field.
- Versatility: Works with any MoGraph setup—cloners, effectors, or fields driving parameters.
🛠️ How to Use It
- Add a Python Field in Cinema 4D.
- Paste the script into the Python Field’s code editor.
- Create User Data controls on the Field object with the following IDs:
- Start Frame (ID 1) – When fading begins.
- Fade Duration (ID 2) – How long the fade lasts.
- Random Seed (ID 3) – Controls randomness.
- Use Position Seed (ID 4) – Toggles position-based randomness.
- Target Min/Max (ID 5 & 6) – Value range for mapping.
- Fade Mode (ID 7) – Select easing type.
- Invert Fade (ID 8) – Reverse fade direction.
- Quantize Steps (ID 9) – Number of steps for quantization.
- Transition Style (ID 10) – Choose between uniform fade or random dissolve.
- Apply the Python Field to any MoGraph setup (e.g., Cloner, Effector, Shader Field).
- Adjust parameters to achieve the desired fade/dissolve effect.
🌐 Practical Applications
- Logo reveals: Smoothly fade in elements or dissolve them randomly for a glitchy effect.
- Particle systems: Control particle visibility with random thresholds for organic dispersal.
- Architectural animations: Sequentially dissolve building components for construction-style reveals.
- Data visualization: Fade in/out bars, points, or graphs with stepped quantization for clarity.
- Abstract art: Create evolving patterns with randomized dissolves and easing curves.

🚀 Conclusion
This script transforms Cinema 4D’s Field system into a powerful procedural animation engine. By combining fade timing, easing, randomness, and quantization, you gain complete control over how clones and field values evolve over time. Whether you’re working on motion graphics, VFX, or abstract art, this tool can save time and unlock creative possibilities.
import c4d
import random
import math
# --- DEBUG HELPER ---
def get_ud_debug(op, id, name, default):
"""Retrieves User Data and prints to Console if missing."""
val = op[c4d.ID_USERDATA, id]
if val is None:
# Limit print spam by checking frame only occasionally in a real scenario,
# but here we rely on the main loop check.
return default
return val
def Sample(op: c4d.modules.mograph.FieldObject, inputs: c4d.modules.mograph.FieldInput,
outputs: c4d.modules.mograph.FieldOutputBlock, info: c4d.modules.mograph.FieldInfo) -> bool:
# --- 1. Setup ---
doc_safe = op.GetDocument()
if not doc_safe: return True
fps = doc_safe.GetFps()
currentFrame = doc_safe.GetTime().GetFrame(fps)
# --- 2. Retrieve User Data ---
# Timing
fadeStartFrame = get_ud_debug(op, 1, "Start Frame", 0)
fadeDuration = get_ud_debug(op, 2, "Fade Duration", 50)
# Randomness
randomSeed = get_ud_debug(op, 3, "Random Seed", 12345)
usePositionSeed = get_ud_debug(op, 4, "Use Pos Seed", True)
# Value Range
targetMin = get_ud_debug(op, 5, "Target Min", 0.0)
targetMax = get_ud_debug(op, 6, "Target Max", 1.0)
# Easing
fadeMode = get_ud_debug(op, 7, "Fade Mode", 0)
invertFade = get_ud_debug(op, 8, "Invert Fade", False)
# --- NEW FEATURES ---
# ID 9: Quantize Steps (0 or 1 = Smooth, 2+ = Stepped)
quantizeSteps = get_ud_debug(op, 9, "Quantize Steps", 0)
# ID 10: Transition Style (0 = Uniform Fade, 1 = Random Dissolve)
transitionStyle = get_ud_debug(op, 10, "Trans Style", 0)
# --- Safety Checks ---
if fadeDuration <= 0: fadeDuration = 1
# Debug print on frame 0
if currentFrame == 0:
print(f"Field: {op.GetName()} | Frame: 0 | Duration: {fadeDuration}")
# --- 3. Calculate Global Progress (0.0 to 1.0) ---
if currentFrame < fadeStartFrame:
p = 0.0
elif currentFrame >= fadeStartFrame + fadeDuration:
p = 1.0
else:
p = float(currentFrame - fadeStartFrame) / float(fadeDuration)
# --- 4. Apply Easing ---
t = p
if fadeMode == 0: t = p # Linear
elif fadeMode == 1: t = p * p # Quad In
elif fadeMode == 2: t = p * (2.0 - p) # Quad Out
elif fadeMode == 3: t = p * p * (3.0 - 2.0 * p) # Smooth
elif fadeMode == 4: t = 2.0**(10 * (p - 1)) if p > 0 else 0 # Expo In
elif fadeMode == 5: t = -(2.0**(-10 * p)) + 1 if p < 1 else 1 # Expo Out
elif fadeMode == 6: # Overshoot
c1 = 1.70158; c3 = c1 + 1; invP = p - 1.0
t = 1.0 + c3 * (invP ** 3) + c1 * (invP ** 2)
# Clamp easing for safety (overshoot can go below 0 or above 1)
# Usually for Fields we want to clamp unless specifically driving position offset
# but for general strength 0-1 is safer.
# t = max(0.0, min(1.0, t))
# --- 5. Processing Loop ---
positions = inputs._position
count = len(positions)
randomValues = [0.0] * count
for i in range(count):
pos = positions[i]
# --- A. Seed Generation ---
if usePositionSeed:
x_int = int(pos.x * 1000)
y_int = int(pos.y * 1000)
z_int = int(pos.z * 1000)
# Use Python's hash for better distribution
posHash = hash((x_int, y_int, z_int))
seed = int(randomSeed) + posHash
else:
seed = int(randomSeed) + i
random.seed(seed)
# --- B. Generate Base Random (0.0 to 1.0) ---
# We generate two numbers: one for the Value, one for the Dissolve threshold
# This ensures the value doesn't change when we switch transition modes.
rnd_value = random.random() # The strength of the clone
rnd_threshold = random.random() # The timing of the clone (used for Dissolve)
# --- C. Apply Quantize (Stepping) ---
# If steps > 1, snap the random value to the nearest step
if quantizeSteps > 1:
steps = float(quantizeSteps - 1)
if steps > 0:
rnd_value = math.floor(rnd_value * quantizeSteps) / steps
# --- D. Calculate Intensity based on Transition Style ---
intensity = 0.0
# Logic: 0.0 = Off, 1.0 = On
# If Invert is False: We go from 1.0 -> 0.0
# If Invert is True: We go from 0.0 -> 1.0
current_t = t # The eased time
if transitionStyle == 0:
# --- UNIFORM FADE ---
# Everyone fades strength together
if invertFade:
intensity = current_t
else:
intensity = 1.0 - current_t
else:
# --- RANDOM DISSOLVE ---
# Clones turn on/off based on their random threshold vs time
if invertFade:
# Growing: Turn ON if time > threshold
intensity = 1.0 if current_t >= rnd_threshold else 0.0
else:
# Dying: Turn OFF if time > threshold
intensity = 1.0 if current_t < rnd_threshold else 0.0
# --- E. Final Mapping ---
# Range Map: Min + (NormalizedRandom * Range)
mappedValue = targetMin + (rnd_value * (targetMax - targetMin))
# Apply the calculated intensity (Fade/Dissolve)
finalValue = mappedValue * intensity
randomValues[i] = finalValue
outputs._value = randomValues
outputs.ClearDeactivated(False)
return True