Creative Computation DSL now supports real-time interactive visualization! Watch your simulations come to life with smooth, controllable playback.
from creative_computation.stdlib.field import field
from creative_computation.stdlib.visual import visual
def my_simulation():
"""Generator that yields frames."""
temp = field.random((128, 128), seed=42)
while True:
temp = field.diffuse(temp, rate=0.2, dt=0.1)
yield visual.colorize(temp, palette="fire")
# Run interactively
gen = my_simulation()
visual.display(lambda: next(gen), title="My Simulation")# Run with interactive display (when available in DSL)
ccdsl run examples/heat_diffusion_animated.ccdsl --steps 100| Key | Action |
|---|---|
| SPACE | Pause/Resume simulation |
| → (Right Arrow) | Step forward one frame (when paused) |
| ↑ (Up Arrow) | Increase simulation speed (+5 FPS) |
| ↓ (Down Arrow) | Decrease simulation speed (-5 FPS) |
| Q or ESC | Quit simulation |
The visualization window shows:
- Current FPS (actual / target)
- Frame count (when paused)
- Status (RUNNING or PAUSED)
- Controls reminder
Display simulation in real-time interactive window.
def display(
frame_generator: Callable[[], Optional[Visual]],
title: str = "Creative Computation DSL",
target_fps: int = 30,
scale: int = 2
) -> NoneParameters:
-
frame_generator: Callable that returns Visual frames- Should return
Visualobject for each frame - Return
Noneto end simulation - Can be a lambda wrapping a generator:
lambda: next(gen)
- Should return
-
title: Window title (default: "Creative Computation DSL") -
target_fps: Target frame rate in frames per second (default: 30)- Can be adjusted during simulation with ↑↓ keys
- Actual FPS may be lower if computation is expensive
-
scale: Display scale factor (default: 2)- Multiplier for visual resolution
- Use larger values for small grids (e.g., 128×128)
- Use smaller values for large grids (e.g., 512×512)
Returns: None (blocks until window is closed)
Raises:
ImportError: If pygame is not installedTypeError: If frame_generator doesn't return Visual
from creative_computation.stdlib.field import field
from creative_computation.stdlib.visual import visual
def heat_diffusion():
temp = field.random((128, 128), seed=42, low=0.0, high=1.0)
while True:
temp = field.diffuse(temp, rate=0.2, dt=0.1, iterations=20)
temp = field.boundary(temp, spec="reflect")
yield visual.colorize(temp, palette="fire")
gen = heat_diffusion()
visual.display(
frame_generator=lambda: next(gen),
title="Heat Diffusion",
target_fps=30,
scale=4
)def limited_simulation():
temp = field.random((64, 64), seed=42)
for i in range(100): # Only 100 frames
temp = field.diffuse(temp, rate=0.1, dt=0.1)
yield visual.colorize(temp, palette="viridis")
# Return None to signal end
return None
gen = limited_simulation()
visual.display(lambda: next(gen, None)) # Use next() with defaultdef multi_field_visualization():
"""Show different fields each second."""
temps = [
field.random((128, 128), seed=i, low=0.0, high=1.0)
for i in range(5)
]
frame = 0
while True:
# Switch field every 30 frames (1 second at 30 FPS)
idx = (frame // 30) % len(temps)
temp = temps[idx]
temp = field.diffuse(temp, rate=0.1, dt=0.1)
temps[idx] = temp # Update in list
yield visual.colorize(temp, palette="fire")
frame += 1
gen = multi_field_visualization()
visual.display(lambda: next(gen))-
Choose appropriate grid size
- 128×128: Fast, good for testing (~60 FPS)
- 256×256: Good balance (~30 FPS)
- 512×512: High quality (~10 FPS)
-
Adjust scale for visibility
- Small grids (64-128): use
scale=4 - Medium grids (256): use
scale=2 - Large grids (512+): use
scale=1
- Small grids (64-128): use
-
Tune iteration counts
- Fewer iterations = faster but less accurate
- Start with 10-20 iterations for diffusion
- Use 20-40 for projection
-
Choose palettes wisely
fire: Hot/cold phenomena (temperature, energy)viridis: General purpose, colorblind-safecoolwarm: Diverging data (positive/negative)grayscale: Simple, high contrast
-
Set appropriate value ranges
vis = visual.colorize(field, palette="fire", vmin=0.0, vmax=1.0)
- Fixed ranges maintain consistent colors
- Auto ranges (default) adapt to data
-
Add visual feedback
- Use title to describe simulation
- Show parameter values in title
visual.display( lambda: next(gen), title=f"Diffusion (rate={rate}, dt={dt})" )
def safe_simulation():
try:
temp = field.random((128, 128), seed=42)
while True:
temp = field.diffuse(temp, rate=0.1, dt=0.1)
yield visual.colorize(temp, palette="fire")
except KeyboardInterrupt:
print("Simulation interrupted by user")
return None
except Exception as e:
print(f"Error in simulation: {e}")
return None
gen = safe_simulation()
visual.display(lambda: next(gen, None))Problem: ImportError: pygame is required
Solution:
pip install pygameProblem: FPS much lower than target
Solutions:
- Reduce grid size
- Decrease iteration counts
- Reduce scale factor
- Profile your code to find bottlenecks
Problem: Blocky visualization
Solution: Increase scale factor or grid resolution
# Use larger scale for small grids
visual.display(..., scale=4)
# Or use larger grid
field.random((256, 256), ...)Problem: Washed out or oversaturated
Solution: Set explicit value ranges
# Clamp to known physical range
vis = visual.colorize(temp, palette="fire", vmin=0.0, vmax=100.0)class SimulationController:
def __init__(self):
self.temp = field.random((128, 128), seed=42)
self.running = True
def get_frame(self):
if not self.running:
return None
self.temp = field.diffuse(self.temp, rate=0.1, dt=0.1)
return visual.colorize(self.temp, palette="fire")
controller = SimulationController()
visual.display(controller.get_frame)def simulation_with_save():
temp = field.random((128, 128), seed=42)
frame_count = 0
while frame_count < 1000:
temp = field.diffuse(temp, rate=0.1, dt=0.1)
vis = visual.colorize(temp, palette="fire")
# Save every 10th frame
if frame_count % 10 == 0:
visual.output(vis, path=f"frames/frame_{frame_count:04d}.png")
frame_count += 1
yield vis
gen = simulation_with_save()
visual.display(lambda: next(gen, None))