You are browsing as a guest. Sign up (or log in) to start making projects!

GraSim

  • 5 Devlogs
  • 7 Total hours

A real-time N-body gravity sandbox built in C++ and compiled to WebAssembly. Spawn planets, watch them orbit, crash them together, trigger supernovas, drop a black hole into the middle of a solar system. Runs in the browser.

Ship #1

**what did i make?**

i made **GraSim**, a browser-based n-body gravity sandbox built in c++ with raylib and compiled to webassembly.

basically you drag to launch planets, spawn stars, drop black holes, make neutron stars, toggle collisions, and watch gravity do increasingly stupid things.

bodies orbit, slingshot, collide, merge, fragment, go supernova, and sometimes collapse into neutron stars or black holes. with collisions off it becomes an orbit painting machine. with collisions on it becomes a disaster generator.

the best part is that most of the cool stuff is not scripted. binary stars can spiral together, merge into a giant star, explode, leave behind a neutron star, and then that neutron star starts ripping through nearby bodies. i did not hard-code that sequence. the rules just line up and it happens.

**what was challenging?**

the hardest part was making the math feel less cursed while still keeping it fun.

at first the sim was fine with small scenes, but bigger ones got expensive really fast because checking every body against every other body is `o(n²)`. so i added **barnes-hut**, which uses a quadtree to treat far-away clusters as one mass. that makes bigger systems way more doable.

then i changed the integrator to **velocity verlet**, which sounds fancy but basically means orbits drift less and feel cleaner.

supernovas were also weird. i wanted massive stars to explode, but not every explosion should leave the same thing behind. now stars below 2000 mass leave no compact remnant, stars from 2000 to 2599 leave a neutron star, and stars above that collapse into black holes.

the funniest bug was in real mode. high-speed collisions could fragment bodies, but the fragments counted as real bodies too. then those fragments collided, made more fragments, and suddenly one small-ish collision made like 4500 bodies. very cool. very bad. so now most debris is visual particles, and only a few chunks become real gravity bodies.

**what am i proud of?**

i’m proud that it feels alive.

not alive like polished-game alive, but like “i gave it some rules and now it is doing things i did not directly tell it to do.”

sometimes two stars spiral together and explode. sometimes a neutron star tears a planet into glowing debris. sometimes a black hole drifts through the mess and eats the aftermath. sometimes turning collisions off makes these giant braided trail paintings across the screen.

i’m also proud that the code is less cursed now. the project is split into main loop, simulation, and rendering files, so changing physics does not mean digging through one giant file full of drawing code.

also it just looks sick sometimes, which is important.

**what should people know so they can test it?**

you can play it in the browser.

controls:

- left drag: launch a planet
- right drag: launch a heavy star
- mouse wheel: zoom
- middle drag: pan
- `a`: asteroid burst
- `b`: binary stars
- `h`: black hole
- `w`: white hole
- `n`: neutron star
- `m`: toggle sandbox / realism mode
- `x`: toggle collisions
- `f`: gravity field rings
- `1 / 2 / 3`: time speed
- `space`: pause
- `r`: reset
- `c`: clear

things to try:

spawn binary stars with `b` and let them spiral.

drop a neutron star into a busy area with `n`.

turn collisions off with `x` and make orbit art.

turn on gravity fields with `f` if you want to see why everything is being pulled around.

switch to real mode with `m` if you want less sandbox nonsense. stay in sandbox mode if you want white holes and maximum chaos.

basically: poke it, break it, and wait for the universe to do something dramatic.

  • 5 devlogs
  • 7h
Try project → See source code →
Open comments for this post

43m logged

Devlog #5: real mode, smaller black holes, and the 4500-body incident

this update started with a dangerous sentence:

“can we make it as real as possible?”

which is always how you end up rewriting half the rules of your tiny gravity sandbox.

the big new thing is realism mode. sandbox mode is still the weird toybox: fixed black holes, white holes, dramatic nonsense, maximum chaos. real mode is stricter. white holes get removed, black holes are no longer pinned in place, and compact objects actually move under gravity like everything else. black holes are still terrifying, but now they can drift, get tugged around, and become part of the system instead of being an immovable stage prop.

i also split visual size from collision size.

before, a black hole’s visible radius and collision radius were basically the same idea. that made them feel too chunky. now bodies have a visual radius and a collision radius. stars and planets can still look big and readable, but black holes can have a smaller actual eating zone while keeping the dramatic accretion disk visuals. neutron stars stay tiny and dense, which feels much better.

then came high-speed realism collisions.

in real mode, fast planet/star impacts can now fragment instead of just merging. the impact throws out visual debris and a few physical asteroid chunks, so collisions feel more like violent breakup events instead of every crash becoming one bigger blob.

and then i discovered a very funny problem.

the launched fragments counted as bodies. those bodies immediately collided with each other. then those collisions made more bodies. then those bodies collided too. a small-ish crash could casually become 4500 bodies and turn the browser into soup.

so that got fixed.

impact debris is now mostly visual particles, with only a small capped number of real chunks. asteroid fragments also cannot recursively fragment again, which means you still get the cool spray of debris without accidentally inventing an infinite universe generator.

the sim now has a clearer split:

  • sandbox mode is for cosmic nonsense
  • real mode is for cleaner physical behavior
  • visual particles are for spectacle
  • real bodies are for things that should actually affect gravity

that last distinction matters a lot. not every glowing speck deserves to be in the Barnes-Hut tree, emotionally or computationally.

the best moments now are these messy almost-real disasters: a star explodes, leaves a neutron star, nearby bodies get torn up, a black hole drifts through the debris, and the whole thing looks like some cursed astronomy textbook diagram that learned how to party.

next i probably want capture tools: hide UI, follow-cam, maybe a screenshot-friendly slow motion key. because the sim is now very good at making moments that disappear right before i can take a picture.

thanks for reading :)

Devlog #5: real mode, smaller black holes, and the 4500-body incident

this update started with a dangerous sentence:

“can we make it as real as possible?”

which is always how you end up rewriting half the rules of your tiny gravity sandbox.

the big new thing is realism mode. sandbox mode is still the weird toybox: fixed black holes, white holes, dramatic nonsense, maximum chaos. real mode is stricter. white holes get removed, black holes are no longer pinned in place, and compact objects actually move under gravity like everything else. black holes are still terrifying, but now they can drift, get tugged around, and become part of the system instead of being an immovable stage prop.

i also split visual size from collision size.

before, a black hole’s visible radius and collision radius were basically the same idea. that made them feel too chunky. now bodies have a visual radius and a collision radius. stars and planets can still look big and readable, but black holes can have a smaller actual eating zone while keeping the dramatic accretion disk visuals. neutron stars stay tiny and dense, which feels much better.

then came high-speed realism collisions.

in real mode, fast planet/star impacts can now fragment instead of just merging. the impact throws out visual debris and a few physical asteroid chunks, so collisions feel more like violent breakup events instead of every crash becoming one bigger blob.

and then i discovered a very funny problem.

the launched fragments counted as bodies. those bodies immediately collided with each other. then those collisions made more bodies. then those bodies collided too. a small-ish crash could casually become 4500 bodies and turn the browser into soup.

so that got fixed.

impact debris is now mostly visual particles, with only a small capped number of real chunks. asteroid fragments also cannot recursively fragment again, which means you still get the cool spray of debris without accidentally inventing an infinite universe generator.

the sim now has a clearer split:

  • sandbox mode is for cosmic nonsense
  • real mode is for cleaner physical behavior
  • visual particles are for spectacle
  • real bodies are for things that should actually affect gravity

that last distinction matters a lot. not every glowing speck deserves to be in the Barnes-Hut tree, emotionally or computationally.

the best moments now are these messy almost-real disasters: a star explodes, leaves a neutron star, nearby bodies get torn up, a black hole drifts through the debris, and the whole thing looks like some cursed astronomy textbook diagram that learned how to party.

next i probably want capture tools: hide UI, follow-cam, maybe a screenshot-friendly slow motion key. because the sim is now very good at making moments that disappear right before i can take a picture.

thanks for reading :)

Replying to @overcharged-coder

0
2
Open comments for this post

1h 20m 56s logged

Devlog #4: neutron stars, cleaner orbits, and cosmic nonsense

this update started as “fix the math a little.”

then neutron stars got involved.

after the last devlog, the sim already had Barnes-Hut gravity, spatial hashing, black holes, white holes, and supernovas. it could throw hundreds of bodies around, but the math needed to feel less cursed. so first i split the project up: the main loop, renderer, and simulation code now live in separate files. boring maintenance, but very worth it. changing gravity code is much nicer when it is not buried inside the draw loop.

then i changed the integrator.

before, bodies used a simpler velocity/position update. now the sim uses velocity Verlet. it calculates acceleration, moves bodies using current velocity plus half the acceleration term, recalculates acceleration, then updates velocity using the average. the result is better orbit behavior and less fake energy drift.

Barnes-Hut got a correctness fix too. a body could be inside a quadtree node that still got approximated as a far-away cluster, which meant it could get a tiny pull from a group containing itself. now internal nodes containing the target body are never approximated. the tree has to recurse deeper, so the gravity math is cleaner.

now the fun part: neutron stars.

neutron stars are compact, bright, and violent. they have a tiny fixed radius, pulse with blue-white halos, and shoot narrow rotating beams like little cosmic lighthouses. they are also dense enough to rip apart nearby bodies.

i added tidal disruption: when a normal body passes inside a neutron star’s Roche-like limit, it gets shredded into hot debris instead of simply merging. the neutron star absorbs some of the mass, a shock ring flashes outward, and particles spray away with the victim’s old velocity mixed into the blast. it looks nasty in the exact way i wanted.

supernovas now tie into that properly. before, the remnant logic was too simple. now it works like this:

  • below 2000 mass: the star blows apart and leaves no compact remnant
  • 2000 to 2599 mass: it collapses into a neutron star
  • 2600+ mass: it collapses into a black hole

so a massive star can explode and leave behind a neutron star, but only in the right range. if it is too massive, it becomes a black hole instead.

the emergent sequences are getting really good now. binary stars spiral together, merge, cross the supernova threshold, explode, and leave a neutron star in the debris. then that neutron star starts tearing through nearby bodies, throwing off little streams of glowing fragments. sometimes a white hole shoves the whole cloud sideways. sometimes a black hole eats the aftermath. none of this is scripted. it just happens because the rules are finally interacting.

with collisions off, the screen turns into these huge braided orbit paintings. with collisions on, it becomes a machine for making disasters.

next i want to make it easier to capture the good moments: probably a hide-UI key, better presets, and maybe a follow-cam so one chaotic object can become the main character.

thanks for reading :)

Devlog #4: neutron stars, cleaner orbits, and cosmic nonsense

this update started as “fix the math a little.”

then neutron stars got involved.

after the last devlog, the sim already had Barnes-Hut gravity, spatial hashing, black holes, white holes, and supernovas. it could throw hundreds of bodies around, but the math needed to feel less cursed. so first i split the project up: the main loop, renderer, and simulation code now live in separate files. boring maintenance, but very worth it. changing gravity code is much nicer when it is not buried inside the draw loop.

then i changed the integrator.

before, bodies used a simpler velocity/position update. now the sim uses velocity Verlet. it calculates acceleration, moves bodies using current velocity plus half the acceleration term, recalculates acceleration, then updates velocity using the average. the result is better orbit behavior and less fake energy drift.

Barnes-Hut got a correctness fix too. a body could be inside a quadtree node that still got approximated as a far-away cluster, which meant it could get a tiny pull from a group containing itself. now internal nodes containing the target body are never approximated. the tree has to recurse deeper, so the gravity math is cleaner.

now the fun part: neutron stars.

neutron stars are compact, bright, and violent. they have a tiny fixed radius, pulse with blue-white halos, and shoot narrow rotating beams like little cosmic lighthouses. they are also dense enough to rip apart nearby bodies.

i added tidal disruption: when a normal body passes inside a neutron star’s Roche-like limit, it gets shredded into hot debris instead of simply merging. the neutron star absorbs some of the mass, a shock ring flashes outward, and particles spray away with the victim’s old velocity mixed into the blast. it looks nasty in the exact way i wanted.

supernovas now tie into that properly. before, the remnant logic was too simple. now it works like this:

  • below 2000 mass: the star blows apart and leaves no compact remnant
  • 2000 to 2599 mass: it collapses into a neutron star
  • 2600+ mass: it collapses into a black hole

so a massive star can explode and leave behind a neutron star, but only in the right range. if it is too massive, it becomes a black hole instead.

the emergent sequences are getting really good now. binary stars spiral together, merge, cross the supernova threshold, explode, and leave a neutron star in the debris. then that neutron star starts tearing through nearby bodies, throwing off little streams of glowing fragments. sometimes a white hole shoves the whole cloud sideways. sometimes a black hole eats the aftermath. none of this is scripted. it just happens because the rules are finally interacting.

with collisions off, the screen turns into these huge braided orbit paintings. with collisions on, it becomes a machine for making disasters.

next i want to make it easier to capture the good moments: probably a hide-UI key, better presets, and maybe a follow-cam so one chaotic object can become the main character.

thanks for reading :)

Replying to @overcharged-coder

0
1
Open comments for this post

1h 34m 46s logged

Devlog #3: black holes, supernovas, and O(N log N)

a lot has happened since the tunneling fix.

last time i said the next few updates would focus on cutting time complexity. that’s done now. the big one is Barnes-Hut. instead of checking every body against every other body for gravity, you build a quadtree that recursively splits space into four quadrants. when computing the gravitational pull on a body, if a whole cluster of bodies is far enough away, you just treat it as a single point mass. “far enough” is a ratio called θ; i used 0.5. gravity goes from O(N²) to O(N log N). at ten bodies you won’t feel it. at two hundred you really will.

collision detection got the same treatment. spatial hashing: bodies are bucketed into grid cells by position and you only check pairs that share a cell. O(N) average case. there was a subtle bug where i had a fixed cell size of 100px, which completely broke for large merged bodies since their combined radius could exceed 100 and the system just never detected them. fixed with cellSize = max(100, maxR * 2).

also switched trails from vector::erase(begin()) to a deque so pop_front() is O(1) instead of O(N) every frame. small thing but it was just sitting there being wasteful.

then the actually fun stuff.

i added a body type system. asteroids are jagged 7-sided polygons with a faint white outline. planets get an atmospheric halo and an equatorial band. stars have layered corona glows and eight slowly rotating rays. bodies auto-upgrade when they absorb enough mass, past 20 you become a planet, past 200 a star. it all just happens from collisions.

black holes (H key) are fixed in space. i zero their acceleration and velocity every substep so gravity from other bodies does nothing to them. visually they’re a black void ringed by a spinning orange accretion disk. they eat everything that enters their radius unconditionally, regardless of whether collision mode is on. they grow as they feed.

white holes (W key) are also fixed. the Barnes-Hut tree naturally computes attraction toward everything including white holes. after the tree pass i run a correction that subtracts 2× each white hole’s gravitational contribution from every other body, cancels the attraction, and flips it to repulsion. in merge mode they also kick anything that gets too close instead of absorbing it.

and then supernovas. when a star hits 2000 mass, it explodes. 22 asteroid fragments fly out in a ring, every nearby body gets an outward shockwave kick proportional to how close they were, and a visual ring expands and fades over about 1.5 seconds. if the star was over 3000 mass it collapses into a black hole instead of disappearing.

the thing i didn’t plan was the emergent sequence: binary stars spiral inward from mutual gravity, merge into one massive star, cross the supernova threshold, explode, and leave a black hole sitting in the middle of the debris field. i didn’t script that. it just happens.

next i want nebulas: regions that apply drag to passing bodies. and probably a follow-cam that locks onto a selected body.

thanks for reading :)

Devlog #3: black holes, supernovas, and O(N log N)

a lot has happened since the tunneling fix.

last time i said the next few updates would focus on cutting time complexity. that’s done now. the big one is Barnes-Hut. instead of checking every body against every other body for gravity, you build a quadtree that recursively splits space into four quadrants. when computing the gravitational pull on a body, if a whole cluster of bodies is far enough away, you just treat it as a single point mass. “far enough” is a ratio called θ; i used 0.5. gravity goes from O(N²) to O(N log N). at ten bodies you won’t feel it. at two hundred you really will.

collision detection got the same treatment. spatial hashing: bodies are bucketed into grid cells by position and you only check pairs that share a cell. O(N) average case. there was a subtle bug where i had a fixed cell size of 100px, which completely broke for large merged bodies since their combined radius could exceed 100 and the system just never detected them. fixed with cellSize = max(100, maxR * 2).

also switched trails from vector::erase(begin()) to a deque so pop_front() is O(1) instead of O(N) every frame. small thing but it was just sitting there being wasteful.

then the actually fun stuff.

i added a body type system. asteroids are jagged 7-sided polygons with a faint white outline. planets get an atmospheric halo and an equatorial band. stars have layered corona glows and eight slowly rotating rays. bodies auto-upgrade when they absorb enough mass, past 20 you become a planet, past 200 a star. it all just happens from collisions.

black holes (H key) are fixed in space. i zero their acceleration and velocity every substep so gravity from other bodies does nothing to them. visually they’re a black void ringed by a spinning orange accretion disk. they eat everything that enters their radius unconditionally, regardless of whether collision mode is on. they grow as they feed.

white holes (W key) are also fixed. the Barnes-Hut tree naturally computes attraction toward everything including white holes. after the tree pass i run a correction that subtracts 2× each white hole’s gravitational contribution from every other body, cancels the attraction, and flips it to repulsion. in merge mode they also kick anything that gets too close instead of absorbing it.

and then supernovas. when a star hits 2000 mass, it explodes. 22 asteroid fragments fly out in a ring, every nearby body gets an outward shockwave kick proportional to how close they were, and a visual ring expands and fades over about 1.5 seconds. if the star was over 3000 mass it collapses into a black hole instead of disappearing.

the thing i didn’t plan was the emergent sequence: binary stars spiral inward from mutual gravity, merge into one massive star, cross the supernova threshold, explode, and leave a black hole sitting in the middle of the debris field. i didn’t script that. it just happens.

next i want nebulas: regions that apply drag to passing bodies. and probably a follow-cam that locks onto a selected body.

thanks for reading :)

Replying to @overcharged-coder

0
1
Open comments for this post

37m 53s logged

Devlog #2: Zoom, stars, and chaos
new updates to GraSim!

zooming is obviously an essential part of GraSim, so I added it as quickly as possible. there are some randomly put stars around the map. zooming introduced a crucial bug:

since you drag to add speed, when you zoom out the max of 10x the max speed gets 10x higher. this caused an issue called tunneling! each frame, the simulation moves bodies velocity * dt. at very high speeds, as mentioned above, a body can actually travel faster than it’s own diameter in a single frame! so, in frame N if they are apart, in frame N+1 they could well be already crossed.

i fixed this with substeps. basically, instead of checking just per frame, we check for collisions every X times per frame. more times checked = less distance possible, so it’s the easiest way of stopping passing into each other.

unfortunately, gravity is already expensive with O(N^2), and substeps adds another O(N^2) on top of that. the next few updates will likely focus on cutting down this time complexity as well as the time complexity of other features noticeably!

thank you so much for reading :)

Devlog #2: Zoom, stars, and chaos
new updates to GraSim!

zooming is obviously an essential part of GraSim, so I added it as quickly as possible. there are some randomly put stars around the map. zooming introduced a crucial bug:

since you drag to add speed, when you zoom out the max of 10x the max speed gets 10x higher. this caused an issue called tunneling! each frame, the simulation moves bodies velocity * dt. at very high speeds, as mentioned above, a body can actually travel faster than it’s own diameter in a single frame! so, in frame N if they are apart, in frame N+1 they could well be already crossed.

i fixed this with substeps. basically, instead of checking just per frame, we check for collisions every X times per frame. more times checked = less distance possible, so it’s the easiest way of stopping passing into each other.

unfortunately, gravity is already expensive with O(N^2), and substeps adds another O(N^2) on top of that. the next few updates will likely focus on cutting down this time complexity as well as the time complexity of other features noticeably!

thank you so much for reading :)

Replying to @overcharged-coder

0
1
Open comments for this post

3h 5m 59s logged

Devlog #1: GraSim v0
first version of GraSim! a fun, addicting gravity simulator!

v0 includes a few basic features, including asteroids, planets/stars, collision/no collision modes, gravity field mode (you can see the gravity fields), and panning mode (infinite map style).

raylib is the easiest for these kinds of things in c++, so i used that as the main library. the majority of the code was just physics, mainly Softened Newtonian Gravity:

F = G·m₁·m₂ / (d² + ε²)

and various other smaller things during collisions, collisions during no-collision mode, etcetera

i’m not sure what the end result will be like, maybe i’ll add more realistic-looking planets and asteroids, and maybe black holes & white holes.

here’s the main part:
static void AddBody( vector<Body>& bodies, Vector2 position, Vector2 velocity, float mass, Color color ) { Body body; body.position = position; body.velocity = velocity; body.mass = mass; body.radius = RadiusFromMass(mass); body.color = color; bodies.push_back(body); }

i’m not sure what the final goal is, but I hope it’s cool!

Devlog #1: GraSim v0
first version of GraSim! a fun, addicting gravity simulator!

v0 includes a few basic features, including asteroids, planets/stars, collision/no collision modes, gravity field mode (you can see the gravity fields), and panning mode (infinite map style).

raylib is the easiest for these kinds of things in c++, so i used that as the main library. the majority of the code was just physics, mainly Softened Newtonian Gravity:

F = G·m₁·m₂ / (d² + ε²)

and various other smaller things during collisions, collisions during no-collision mode, etcetera

i’m not sure what the end result will be like, maybe i’ll add more realistic-looking planets and asteroids, and maybe black holes & white holes.

here’s the main part:
static void AddBody( vector<Body>& bodies, Vector2 position, Vector2 velocity, float mass, Color color ) { Body body; body.position = position; body.velocity = velocity; body.mass = mass; body.radius = RadiusFromMass(mass); body.color = color; bodies.push_back(body); }

i’m not sure what the final goal is, but I hope it’s cool!

Replying to @overcharged-coder

0
1

Followers

Loading…