Building Conway’s Game of Life in Rust with Pixels and Winit
A beginner-friendly guide to using the Pixels and Winit crates to create a graphical version of Conway’s Game of Life in Rust.
🚧 This post is under construction 🚧
TL;DR
- For beginners
- The Rust workspace is on GitHub
- Use VSCode + Win11 (not tested elsewhere)

Game of Life in Rust using Winit and Pixels crates.
Table of Contents
Introduction
- The project consist of a Rust workspace with 25 different packages
Objectives
A gentle start
When I discussed the idea with ChatGPT and explained that, for teaching purposes, I wanted to build a Game of Life application in Rust and was looking for a helpful library, it kindly pointed me to the Winit and Pixels crates. I then checked out the links below (this is a lie but this what I should have done):
winit
- crate: https://crates.io/crates/winit
- doc: https://docs.rs/winit/0.30.12/winit/
- examples: https://github.com/rust-windowing/winit/tree/master/winit/examples
Pixels
- crate: https://crates.io/crates/pixels
- doc: https://docs.rs/pixels/0.15.0/pixels/
- examples: https://github.com/parasyte/pixels/tree/main/examples
What I learnt: Generally speaking, there’s quite a bit of documentation, the code is available, but there are no tutorials. Furthermore—and it took me a while to figure this out—if you’re looking for an example, you need to go to the GitHub site and check out the examples/
directory.
There has been, and this is really great, a huge effort made in terms of documentation. However, I don’t understand why, unlike the canonical README.md
file, there isn’t a GETTING_STARTED.md
file that would explain the key concepts behind the API, the important points to keep in mind, the mindset needed when approaching the crate… We could, of course, also have some diagrams and additional explanations around the code samples in the examples/
directory.
- Get the code from GitHub
- Open the workspace with VSCode
- Open a terminal (CTRL + ù)
cargo run -p step_00_winit_029

A first window using Rust, Winit and Pixels
Now open the package (project) named step_00_winit_029
. It consists of a Cargo.toml
and a main.rs
files

The first package in the Rust Workspace
Here is Cargo.toml
[package]
name = "step_00_winit_029"
version = "0.1.0"
edition = "2024"
[dependencies]
pixels = "0.15.0"
winit = "0.29"
The issue I have is that it seems Pixels
requires winit
0.29 but this is not the last version. See below what I see:

This is an issue because when you go to the WinIt GitHub page you must make sure you pick the version 0.29 before looking at the examples source code.

Indeed, but I discovered it much later, between 0.29 and 0.30 there are breaking changes in the API but we will address them with the next sample code. For now, let’s study the code below:
use pixels::{Pixels, SurfaceTexture};
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder},
};
const WIDTH: u32 = 200;
const HEIGHT: u32 = 150;
type Error = Box<dyn std::error::Error>;
type Result<T> = std::result::Result<T, Error>;
fn main() -> Result<()> {
let event_loop = EventLoop::new()?;
let mut window: Option<&'static Window> = None;
let mut pixels: Option<Pixels> = None;
event_loop.run(move |event, elwt| {
elwt.set_control_flow(ControlFlow::Poll);
match event {
Event::Resumed => {
if window.is_none() {
let built_window = WindowBuilder::new().with_title("Step_00_winit_029: First try").build(elwt).unwrap();
let scale_factor = built_window.scale_factor();
println!("Scale factor : {}", scale_factor);
let size = built_window.inner_size();
let window_ref: &'static Window = Box::leak(Box::new(built_window));
let surface = SurfaceTexture::new(size.width, size.height, window_ref);
let built_pixels = Pixels::new(WIDTH, HEIGHT, surface).unwrap();
window = Some(window_ref);
pixels = Some(built_pixels);
}
}
Event::WindowEvent {
event: WindowEvent::RedrawRequested, ..
} => {
if let Some(pixels) = &mut pixels {
let frame = pixels.frame_mut();
for pixel in frame.chunks_exact_mut(4) {
pixel[0] = 0x20; // R
pixel[1] = 0x40; // G
pixel[2] = 0xFF; // B
pixel[3] = 0xFF; // A
}
if let Err(err) = pixels.render() {
eprintln!("pixels.render() failed: {err}");
elwt.exit();
}
}
}
Event::AboutToWait => {
window.expect("Bug - Window should exist").request_redraw();
}
Event::WindowEvent {
event: WindowEvent::CloseRequested, ..
} => {
elwt.exit();
}
_ => {}
}
})?;
Ok(())
}
Comments
This is NOT the code I had at the beginning but since I try to tell you a gentle story I’m lying… In fact I spent too much time mixing winit 0.30 with 0.29 API. Nothing was working. I did’nt read the documentation nor checked the dependencies… A nightmare…
The code is 80 lines…