Scripting Library
PhysicsSandbox.Scripting is an F# class library that bundles all the convenience functions
script authors need to interact with the physics sandbox. Instead of referencing 3 DLLs and
4 NuGet packages, scripts reference a single DLL and get everything through an [<AutoOpen>]
Prelude module.
Note: This project uses Spec Kit for specification-driven development. See constitution.md for the governing principles.
Modules
The library is organized into six modules, each with a .fsi signature file
(per Constitution Principle V):
Module |
Functions |
Purpose |
|---|---|---|
|
|
Error handling, timing, thread control |
|
|
Convert between tuples and protobuf Vec3 |
|
|
Build proto SimulationCommand messages |
|
|
Send commands in auto-chunked batches of 100 |
|
|
Simulation state control and ID generation |
|
(all of the above) |
|
Quick Start
A script needs just one #r directive and a few open statements:
#r "nuget: PhysicsSandbox.Scripting"
open PhysicsClient.Session
open PhysicsClient.SimulationCommands
open PhysicsSandbox.Scripting.Prelude
// Connect and go
let s = connect "http://localhost:5180" |> ok
resetSimulation s
let id = nextId "sphere"
let cmd = makeSphereCmd id (0.0, 10.0, 0.0) 0.5 1.0
batchAdd s [cmd]
runFor s 3.0
disconnect s
Scripting/scratch/ and Scripting/scripts/ folders use the same
#r "nuget: PhysicsSandbox.Scripting" reference, so moving a script between them requires no code changes.
Helpers Module
Utility functions for error handling and timing.
ok — Unwrap a Result
Extracts the Ok value or throws with the error message:
let value = Ok 42 |> ok // returns 42
let boom = Error "oops" |> ok // throws System.Exception("oops")
timed — Measure Execution
Wraps a function call with a Stopwatch and prints elapsed time:
let result = timed "my operation" (fun () ->
// ... expensive work ...
42)
// prints: [TIME] my operation: 123 ms
// returns: 42
Vec3Builders Module
Convert between F# tuples and protobuf Vec3 messages.
let v = toVec3 (1.0, 2.0, 3.0) // Vec3 { X=1, Y=2, Z=3 }
let t = toTuple v // (1.0, 2.0, 3.0)
CommandBuilders Module
Build protobuf SimulationCommand messages from simple F# values.
No need to manually construct Shape, AddBody, ApplyImpulse, or ApplyTorque proto objects.
// Add a sphere at position (0, 10, 0) with radius 0.5 and mass 1.0
let sphere = makeSphereCmd "sphere-1" (0.0, 10.0, 0.0) 0.5 1.0
// Add a box at origin with half-extents (0.5, 0.5, 0.5) and mass 2.0
let box = makeBoxCmd "box-1" (0.0, 5.0, 0.0) (0.5, 0.5, 0.5) 2.0
// Apply an upward impulse to a body
let kick = makeImpulseCmd "sphere-1" (0.0, 20.0, 0.0)
// Apply spin around the Y axis
let spin = makeTorqueCmd "box-1" (0.0, 5.0, 0.0)
BatchOperations Module
Send lists of commands efficiently with automatic chunking.
The server enforces a 100-command limit per batch request;
batchAdd handles splitting transparently:
// Create 200 spheres — automatically split into 2 batches of 100
let cmds =
[ for i in 1..200 ->
let id = nextId "sphere"
makeSphereCmd id (0.0, float i * 0.5, 0.0) 0.3 1.0 ]
batchAdd s cmds
Any per-command failures are logged with their index and error message:
[BATCH FAIL] command 42: Body ID already exists
SimulationLifecycle Module
resetSimulation — Clean Slate
Resets the simulation to a pristine state:
- Pause the simulation
- Server-side reset (falls back to manual
clearAllon error) - Reset the ID generator counter
- Add a ground plane
- Set gravity to (0, -9.81, 0)
- Wait 100ms for stabilization
runFor — Timed Execution
Play the simulation for a fixed duration, then pause:
resetSimulation s // clean slate
// ... add bodies ...
runFor s 3.0 // run for 3 seconds, then pause
nextId — Sequential IDs
Generate human-readable, sequential body IDs:
let id1 = nextId "sphere" // "sphere-1"
let id2 = nextId "sphere" // "sphere-2"
let id3 = nextId "box" // "box-1"
Extending the Library
To add a new function:
- Add the signature to the appropriate
.fsifile (e.g.,Vec3Builders.fsi) - Add the implementation to the matching
.fsfile - (Optional) Re-export in
Prelude.fsiandPrelude.fsfor script convenience - Update
SurfaceAreaBaseline.txtin the test project - Rebuild — existing scripts and the MCP server continue to work unchanged
.fsi
signature file. The surface area baseline test will fail if a new public symbol is added without
updating the baseline.
Folder Layout
All scripting folders live under Scripting/ at the repo root:
Folder |
Tracked |
Purpose |
|---|---|---|
|
Git |
Curated, production-quality scripts |
|
Gitignored |
Developer-local experimentation |
|
Git |
F# demo suite (22 demos + runners) |
|
Git |
Python demo suite (22 demos + runners) |
scripts/ and scratch/ use the same relative path to the library DLL,
so moving a script between them requires no code changes.
MCP Server Integration
The MCP server references PhysicsSandbox.Scripting as a project dependency.
ClientAdapter.toVec3 delegates to Vec3Builders.toVec3, eliminating code duplication
between the scripting and MCP tool surfaces.
Next Steps
- Getting Started — build and run the full sandbox
- Demo Scripts — 15 physics demos in F# and Python
- MCP Tools — 59 AI-assisted debugging tools
val float: value: 'T -> float (requires member op_Explicit)
--------------------
type float = System.Double
--------------------
type float<'Measure> = float
PhysicsSandbox