# Temporal and 3D trajectories with ggWebGL

# Purpose

`ggWebGL` can render ordered trajectory/path layers with timeline metadata. The examples below use deterministic in-memory data and browser-side WebGL through `htmlwidgets`; they do not download data or report performance numbers. The widgets autoplay by default and can be paused, scrubbed, or reset with the timeline controls. If a browser throttles a background tab, pressing Play resumes the timeline.

Code examples are shown by default. Live WebGL widgets are disabled during CRAN, package checks, and CI. Rich local or pkgdown rendering requires `GGWEBGL_EVAL_COVERAGE_VIGNETTE=true` and `GGWEBGL_EVAL_LIVE_WIDGETS=true`.

Status. Trajectory paths, trajectory shader encodings, exact/cumulative timeline controls, and Shiny timeline messages are `Experimental` public APIs. They are suitable for renderer evaluation and small examples, but should not be described as stable performance or animation guarantees.

The relevant renderer pieces are:

- `geom_path3d_webgl()` for ordered paths with optional `z`, `frame`, and `time` aesthetics.
- `animation_spec()` / `ggwebgl_timeline()` for exact or cumulative timeline metadata.
- `trajectory_age`, `trajectory_velocity`, and `trajectory_direction` shader modes for renderer-side trajectory visual encodings.
- `input$<outputId>_timeline` and `updateGgWebGLTimeline()` for Shiny timeline integration.

# 2D Cumulative Trajectory

This spiral is rendered as an ordered path with `frame` metadata and cumulative timeline filtering. The `trajectory_age` shader makes the time prefix visible as the timeline is scrubbed.

    temporal_spiral_widget(height = 460)

# 3D Trajectory

The helix uses the same path contract, but supplies `z` and `time` values. The view is opt-in 3D with an orbit camera.

    temporal_helix_widget(height = 460)

# Velocity and Direction Encodings

Trajectory velocity and direction are renderer-side visual encodings computed from the serialized path coordinates and timeline values. They do not add application semantics to the data.

    temporal_velocity_widget(height = 460)

    temporal_direction_widget(height = 460)

# Exact Frame Samples

Exact filtering is useful for point-like animated samples. This example shows only the samples whose `frame` equals the selected timeline value.

    temporal_exact_particles_widget(height = 460)

# Shiny Timeline Integration

In Shiny, the browser emits the current timeline state as `input$<outputId>_timeline`. Server code can update the browser-side timeline with `updateGgWebGLTimeline()`.

    library(shiny)
    library(ggWebGL)

    ui <- fluidPage(
      actionButton("restart", "Reset"),
      ggWebGLOutput("plot", height = "420px"),
      verbatimTextOutput("timeline_state")
    )

    server <- function(input, output, session) {
      output$plot <- renderGgWebGL({
        temporal_helix_widget(height = 460)
      })

      observeEvent(input$restart, {
        updateGgWebGLTimeline(session, "plot", index = 1L, playing = FALSE)
      })

      output$timeline_state <- renderPrint({
        input$plot_timeline
      })
    }

    if (interactive()) {
      shinyApp(ui, server)
    }

The repository also includes a compact optional app at `inst/examples/shiny/timeline-control-demo.R`.

# Limitations

Missing values and group boundaries break paths. Cumulative timeline filtering draws complete visible prefixes; exact path filtering does not interpolate partial hidden segments. Velocity and direction are renderer-side visual encodings. Shiny timeline events report renderer time/frame state; downstream applications remain responsible for interpreting those values.