Skip to contents

boids4R simulates flocking and swarm dynamics in R. The core objects are renderer-neutral: they describe boids, rules, worlds, and recorded frames.

library(boids4R)

sim <- boids_scenario("schooling_2d", n = 80, steps = 20, seed = 12)
frames <- as.data.frame(sim)
head(frames)
#>   frame time         id species           x           y z          vx
#> 1     0    0 boid-00001    boid -2.19958677  1.25804205 0 -0.02683391
#> 2     0    0 boid-00002    boid  0.34519521  0.01271507 0  0.05059609
#> 3     0    0 boid-00003    boid -1.90403899 -0.89780400 0  0.13404070
#> 4     0    0 boid-00004    boid  0.01672697 -0.69175552 0  0.15357851
#> 5     0    0 boid-00005    boid -0.46988986 -0.23499981 0 -0.07141984
#> 6     0    0 boid-00006    boid  0.56104504  0.15811094 0  0.21125602
#>            vy vz      speed
#> 1  0.02341728  0 0.03561499
#> 2 -0.40515990  0 0.40830688
#> 3 -0.13518897  0 0.19037586
#> 4 -0.29706520  0 0.33441604
#> 5  0.09770255  0 0.12102306
#> 6 -0.10675316  0 0.23669674

The same frame table can be handed to visualization packages. If ggWebGL 0.4.0 or later is installed, the optional adapter creates point and velocity-vector primitives with exact timeline controls.

if (requireNamespace("ggWebGL", quietly = TRUE) &&
    utils::packageVersion("ggWebGL") >= "0.4.0") {
  ggWebGL::ggWebGL(as_ggwebgl_spec(sim), height = 440)
}

For larger examples, see the scenario gallery and custom simulation workflow vignettes. They show obstacle corridors, predator avoidance, parameter sweeps, and mixed-species 3D runs using the same renderer-neutral frame output.

To generate a standalone WebGL page for an external browser:

stopifnot(
  requireNamespace("ggWebGL", quietly = TRUE),
  utils::packageVersion("ggWebGL") >= "0.4.0"
)

sim <- boids4R::boids_scenario("murmuration_3d", n = 400, steps = 150, seed = 1)

spec <- boids4R::as_ggwebgl_spec(sim)
spec$render$timeline$autoplay <- TRUE
spec$render$timeline$speed <- 2

w <- ggWebGL::ggWebGL(spec, height = 520)

htmlwidgets::saveWidget(w, "boids_murmuration.html", selfcontained = FALSE)
browseURL(normalizePath("boids_murmuration.html"))

To see trajectories, not only moving current positions, use cumulative line trails:

frames <- as.data.frame(sim)
keep <- unique(frames$id)[1:120]
trail <- frames[frames$id %in% keep, ]

line_layer <- ggWebGL::ggwebgl_layer_lines(
  trail,
  x = "x", y = "y", z = "z",
  group = "id",
  colour = "#334155",
  alpha = 0.08,
  width = 0.7,
  frame = "frame",
  time = "time"
)

point_layer <- ggWebGL::ggwebgl_layer_points(
  frames,
  x = "x", y = "y", z = "z",
  colour = "#2563eb",
  alpha = 0.45,
  size = 2,
  id = "id",
  frame = "frame",
  time = "time"
)

spec <- ggWebGL::ggwebgl_spec(
  list(line_layer, point_layer),
  webgl = list(
    view = ggWebGL::ggwebgl_view("3d", controller = "orbit", projection = "perspective")
  ),
  timeline = ggWebGL::ggwebgl_timeline(
    frames = sort(unique(frames$frame)),
    filter = "cumulative",
    autoplay = TRUE,
    speed = 2
  )
)

htmlwidgets::saveWidget(ggWebGL::ggWebGL(spec, height = 520), "boids_trails.html")
browseURL(normalizePath("boids_trails.html"))