⚠️ Beta State

PyBevy is in an early and experimental stage. The API is incomplete, subject to breaking changes without notice, and you should expect bugs. Many features are still under development.

Wave Animation with the View API

Animate thousands of cubes at 200x the speed of Query iteration.

Introduction

This recipe shows how to animate a large grid of cubes using PyBevy's View API. The View API compiles your Python expressions into bytecode and executes them across all entities in parallel — no Python for loop required.

For a grid of 256,000 cubes, this runs in about 3-4ms per frame, compared to ~740ms with traditional Query iteration.

from pybevy.prelude import *

Marker Component

We tag our animated cubes with a marker component so the View system only processes entities we care about.

@component
class Cube(Component):
    pass

Setup: Spawning the Grid

We spawn cubes in a flat grid, each slightly offset. The grid uses 32 different colors distributed evenly across the cubes.

def setup(
    commands: Commands,
    meshes: ResMut[Assets[Mesh]],
    materials: ResMut[Assets[StandardMaterial]],
) -> None:
    mesh = meshes.add(Cuboid.from_length(0.15))
 
    # Grid parameters
    n = 10_000
    grid_size = 100
    half = grid_size // 2
 
    # Create a palette of colors
    colors = [
        materials.add(Color.srgb(0.3 + 0.7 * (i % 8) / 8, 0.5, 0.3 + 0.7 * (i // 8) / 4))
        for i in range(32)
    ]
 
    # Spawn cubes in a grid
    for i in range(n):
        x = (i % grid_size - half) * 0.25
        z = (i // grid_size - half) * 0.25
        commands.spawn(
            Cube(),
            Mesh3d(mesh),
            MeshMaterial3d(colors[i % 32]),
            Transform.from_xyz(x, 0.0, z),
        )
 
    # Lighting
    commands.spawn(
        DirectionalLight(illuminance=5000.0),
        Transform.IDENTITY.looking_at(Vec3(-1.0, -1.0, -1.0), Vec3.Y),
    )
 
    # Camera positioned to view the wave
    commands.spawn(
        Camera3d(),
        Transform.from_xyz(-5.0, 15.0, 20.0).looking_at(Vec3.ZERO, Vec3.Y),
    )

The Wave Animation System

This is where the View API shines. Instead of writing:

for transform in query:
    transform.translation.y = sin(transform.translation.x + time)

We write a single expression that applies to ALL entities simultaneously:

def move_cubes(view: View[Mut[Transform], With[Cube]], time: Res[Time]) -> None:
    t = time.elapsed_secs()
    transform = view.column_mut(Transform)
 
    # Two overlapping sine waves create a ripple effect
    x_wave = (transform.translation.x * 0.5 + t * 3.0).sin()
    z_wave = (transform.translation.z * 0.4 - t * 2.5).sin()
    transform.translation.y = (x_wave + z_wave) * 2.0

How It Works

  1. view.column_mut(Transform) returns a lazy column handle — no data is copied
  2. Accessing .translation.x builds an expression tree
  3. Calling .sin() adds a sine operation to the expression
  4. The assignment (=) triggers bytecode compilation and parallel execution

The entire grid is updated in a single batch operation, with no Python-level loop.

Running the Example

@entrypoint
def main(app: App) -> App:
    return (
        app
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, move_cubes)
    )
 
if __name__ == "__main__":
    main().run()