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):
passSetup: 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.0How It Works
view.column_mut(Transform)returns a lazy column handle — no data is copied- Accessing
.translation.xbuilds an expression tree - Calling
.sin()adds a sine operation to the expression - 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()