6 releases (3 breaking)

0.12.1 Jun 29, 2024
0.12.0 Jan 27, 2024
0.11.1 Sep 27, 2023
0.11.0 Jul 16, 2023
0.5.0 Nov 9, 2021

#6 in #send-sync

Download history 56/week @ 2024-03-23 11/week @ 2024-03-30 1/week @ 2024-04-06 207/week @ 2024-06-29

207 downloads per month




Crates.io Documentation

Sparsey is a sparse set-based Entity Component System (ECS).

Design Goals

  • Be flexible: Any Send + Sync + 'static type can be used as a component.
  • Be concise: The most commonly used functionalities should require the least amount of typing.
  • Make use of sparse sets: Provide features exclusive to sparse set-based ECS.


use sparsey::prelude::*;

struct Position(f32);
struct Velocity(f32);

fn main() {
    let mut entities = EntityStorage::default();

    entities.create((Position(0), Velocity(1)));
    entities.create((Position(0), Velocity(2)));
    entities.create((Position(0), Velocity(3)));

    entities.run(|mut positions: CompMut<Position>, velocities: Comp<Velocity>| {
        (&mut positions, &velocities).for_each(|(position, velocity)| {
            position.0 += velocity.0;


Easy to Use Systems

Systems are plain functions.

struct HealMultiplier(f32);

fn update_positions(mut positions: CompMut<Position>, velocities: Comp<Velocity>) {
    (&mut positions, &velocities).for_each(|(position, velocity)| {
        position.0 += velocity.0;

fn update_hps(mut hps: CompMut<Hp>, heals: Comp<Heal>, heal_multipler: Res<HealMultiplier>) {
    (&mut hps, &heals).for_each(|(hp, heal)| {
        hp.0 += heal.0 * heal_multiplier.0;

let mut world = World::default();


Expressive Queries

Get, include and exclude components using Sparsey's query API.

fn queries(a: Comp<A>, b: Comp<B>, c: Comp<C>, d: Comp<D>, e: Comp<E>) {
    // Iter components A and B from entities with A and B.
    (&a, &b).for_each(|(a, b)| {
        // ... 

    // Iter components A from entities with A and B.
    (&a).include(&b).for_each(|a| {
        // ...

    // Iter components A from entities with A and without B.
    (&a).exclude(&b).for_each(|a| {
        // ...

    // Iter components A from entities with A and B, without C.
    (&b).include(&b).exclude(&c).for_each(|a| {
        // ...

Great Performance with Grouped Storages

Sparsey allows the user to "group" component storages to greatly optimize iteration performance. Groups are created by setting a GroupLayout.

let layout = GroupLayout::builder()
    .add_group::<(A, B)>()
    .add_group::<(A, B, C, D>)>()

let entities = EntityStorage::new(&layout);

After the layout is set, iterators over the grouped storages become "dense", greatly improving their performance. Additionally, grouped storages allow access to their components and entities as slices.

fn group_slices(a: Comp<A>, b: Comp<B>) {
    if let Some(entities) = (&a, &b).group_entities() {
        // ...

    if let Some((a_slice, b_slice)) = (&a, &b).group_components() {
        // ...

    if let Some((entities, (a_slice, b_slice))) = (&a, &b).group_data() {
        // ...


Sparsey takes inspiration and borrows features from other free and open source ECS projects, namely Bevy, EnTT, Legion, Shipyard and Specs. Make sure you check them out!


Sparsey is dual-licensed under either

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above without any additional terms or conditions.