Machine Learning // Evolutionary Biology

Neural Byte:
2D Evolutionary Predator-Prey Simulation in Rust

An analysis of Neural Byte, a high-performance 2D evolutionary simulation driven by feedforward neural networks and genetic selection algorithms. We examine the math behind sensory-based steering physics, Gaussian mutations, and metabolic dynamics.

T
Tensor R&D Lab
July 15, 2026
10 Min Read

01/ Introduction to Neural Byte

Computational modeling of natural selection requires a combination of high-fidelity physical kinematics and adaptive agency. Neural Byte is a high-performance, 2D evolutionary simulation written in Rust that models the classic predator-prey ecosystem.

Rather than utilizing hardcoded heuristic behaviors (such as basic seek or avoid algorithms), agents in Neural Byte are controlled by independent **Feedforward Neural Networks** (Multi-Layer Perceptrons). These neural controllers map spatial target vectors onto kinematics steering inputs. Over thousands of generations, selective pressures naturally emerge: prey adapt to locate food particles while dodging predators, and predators evolve sophisticated chasing trajectories.

Interactive Simulation Arena

Live WebAssembly Render (Rust + Macroquad + Egui)

Initializing neural-byte.wasm...
WASM Hardware Accelerated

Figure 1: Live evolutionary simulation. Click on individual agents to view active neural paths. Use controls on the right panel to tweak parameters.

03/ Agent Neural Architectures

The brains of the agents are modeled as multi-layer feedforward networks (MLPs). The structure and dimensions of these networks are specialized depending on the agent's ecological role:

Prey Neural Brain

  • Input Layer (4 neurons):
    • Relative local direction to closest food ($X, Y$)
    • Relative local direction to closest predator ($X, Y$)
  • Hidden Layer (8 neurons): Activated via ReLU
  • Output Layer (2 neurons): Activated via Tanh
    • Steering angle force $[-1.0, 1.0]$
    • Engine acceleration throttle $[-1.0, 1.0]$

Predator Neural Brain

  • Input Layer (3 neurons):
    • Relative local direction to closest prey ($X, Y$)
    • Proximity distance to the target prey
  • Hidden Layer (8 neurons): Activated via ReLU
  • Output Layer (2 neurons): Activated via Tanh
    • Steering angle force $[-1.0, 1.0]$
    • Engine acceleration throttle $[-1.0, 1.0]$

Mathematical Activation & Feedforward Propagation

The feedforward propagation for layer $l$ is calculated using bias vector $\mathbf{b}$ and weight matrix $\mathbf{W}$:

$$\mathbf{z}^{[l]} = \mathbf{W}^{[l]} \mathbf{a}^{[l-1]} + \mathbf{b}^{[l]}$$

The hidden nodes use the Rectified Linear Unit (ReLU) activation function, which handles vanishing gradients:

$$f(x) = \max(0, x)$$

To restrict steering force and engine throttle to continuous bounded physical values, output nodes utilize the Hyperbolic Tangent (Tanh) function:

$$\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}$$

04/ Evolutionary Pressures & Reproduction

In Neural Byte, fitness is not explicitly computed using an objective global loss function. Instead, agent reproduction is entirely resource-dependent:

  • Prey: Spawn when their gathered food energy surpasses $100.0$. Spawning divides their energy, passing mutated weights and biases to an offspring.
  • Predators: Consume prey to gain $220.0$ energy. If their energy crosses $700.0$, they reproduce. If their energy reaches $0.0$ (due to metabolism), they die.

Gaussian Mutation Strategy

During mitosis, the offspring inherits its parent's network weights $w_{ij}$ and biases $b_i$, with small variations introduced via a Normal (Gaussian) distribution:

$$w_{\text{offspring}} = w_{\text{parent}} + \mathcal{N}(0, \sigma^2)$$

Where the standard deviation $\sigma$ (mutation rate) is dynamically adjustable in real-time. This variance allows the agents to slowly scale and adapt their navigation.

05/ Neural Network Feedforward in Rust

Below is a clean Rust snippet demonstrating the feedforward process of an agent's multi-layer perceptron:

pub struct Layer {
    pub weights: Vec>,
    pub biases: Vec,
}

pub struct FeedForwardNet {
    pub layers: Vec,
}

impl FeedForwardNet {
    pub fn feedforward(&self, inputs: &[f32]) -> Vec {
        let mut current_outputs = inputs.to_vec();

        for (i, layer) in self.layers.iter().enumerate() {
            let is_output_layer = i == self.layers.len() - 1;
            let mut next_outputs = vec![0.0; layer.biases.len()];

            for j in 0..layer.biases.len() {
                let mut sum = layer.biases[j];
                for k in 0..current_outputs.len() {
                    sum += current_outputs[k] * layer.weights[j][k];
                }
                
                // Hidden layers use ReLU, output layer uses Tanh
                next_outputs[j] = if is_output_layer {
                    sum.tanh()
                } else {
                    sum.max(0.0) // ReLU
                };
            }
            current_outputs = next_outputs;
        }
        current_outputs
    }
}