Force-directed graph simulation with unified D3 and WASM engine support.
Live interactive demo can be seen as part of the suite of Hylograph library demos. Click the screenshot below for the Force Playground.
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
import Data.Nullable (null) as Nullable
import Hylograph.Simulation
( runSimulation, Engine(..), SimulationEvent(..), subscribe
, setup, manyBody, center, withStrength, withX, withY, static
)
import Hylograph.AST as A
import Hylograph.Unified.Attribute as Attr
import Hylograph.Unified.Display (showNumD)
import Hylograph.Internal.Selection.Types (ElementType(..))
main :: Effect Unit
main = do
-- Run simulation with D3 engine (or use WASM for same API)
{ handle, events } <- runSimulation
{ engine: D3
, setup: setup "physics"
[ manyBody "charge" # withStrength (static (-50.0))
, center "center" # withX (static 200.0) # withY (static 150.0)
]
, nodes:
[ { id: 0, x: 190.0, y: 140.0, vx: 0.0, vy: 0.0, fx: Nullable.null, fy: Nullable.null }
, { id: 1, x: 200.0, y: 150.0, vx: 0.0, vy: 0.0, fx: Nullable.null, fy: Nullable.null }
, { id: 2, x: 210.0, y: 160.0, vx: 0.0, vy: 0.0, fx: Nullable.null, fy: Nullable.null }
]
, links: []
, container: "#visualization"
, nodeTemplate: \_ -> A.elem Circle
[ Attr.attr "cx" _.x showNumD
, Attr.attr "cy" _.y showNumD
, Attr.attrStatic "r" "8"
, Attr.attrStatic "fill" "#4a9eff"
]
, alphaMin: 0.001
}
-- Subscribe to simulation events
_ <- subscribe events \event -> case event of
Tick { alpha } -> log $ "Alpha: " <> show alpha
Completed -> log "Simulation converged!"
_ -> pure unit
log "Simulation running!"spago install hylograph-simulation- Dual Engine Support - Same API for D3.js and Rust/WASM physics
- Framework Agnostic - Works with Halogen, React, or vanilla JS
- Declarative Forces - Compose forces with a fluent builder API
- Event Subscription - React to Tick, Started, Stopped, Completed events
- GUP Semantics - Enter/update/exit tracking when data changes
import Hylograph.ForceEngine.Halogen (toHalogenEmitter)
handleAction Initialize = do
{ handle, events } <- liftEffect $ runSimulation config
halogenEmitter <- liftEffect $ toHalogenEmitter events
void $ H.subscribe $ halogenEmitter <#> SimEvent
handleAction (SimEvent (Tick { alpha })) =
H.modify_ _ { alpha = alpha }
handleAction (SimEvent Completed) =
liftEffect $ log "Done!"useEffect(() => {
const { handle, events } = runSimulation(config)();
const unsubscribe = subscribe(events)(event => {
if (event.tag === 'Completed') {
console.log('Simulation converged!');
}
})();
return () => unsubscribe();
}, []);| Force | Description |
|---|---|
manyBody |
N-body charge simulation (attract/repel) |
center |
Pulls nodes toward a center point |
link |
Spring forces between connected nodes |
collide |
Prevents node overlap |
positionX |
Pulls nodes toward an X position |
positionY |
Pulls nodes toward a Y position |
radial |
Pulls nodes toward a circle |
Hylograph.Simulation- High-level API (recommended)Hylograph.Simulation.Emitter- Framework-agnostic event systemHylograph.ForceEngine.Setup- Declarative force configurationHylograph.ForceEngine.Simulation- Low-level D3 simulation controlHylograph.ForceEngine.WASM- WASM engine integration
This package is part of the Hylograph visualization ecosystem:
- hylograph-selection - D3 selection and rendering
- hylograph-simulation - Force simulation (this package)
- hylograph-simulation-halogen - Halogen integration
- hylograph-graph - Graph data structures
- hylograph-layout - Layout algorithms
MIT