Skip to content

An experiment in building a WebXR experience with a functional programming language (OCaml)

Notifications You must be signed in to change notification settings

tommy-xr/citadel-xr

Repository files navigation

Citadel

An experiment in building a WebXR experience in a functional programming language (OCaml)

Unfortunately I don't have a license to re-publish the assets to host an interactive demo, but here is a video:

Demo (in browser, but better in VR):

demo.mp4

Technologies Used

  • OCaml
  • esy
  • BabylonJS - WebXR, WebGL library
  • Ammo - Ammo Physics Engine (emscripten build of the Bullet Physics engine)

Notes

Functional Rendering

This project uses a simple React-style reconciler for describing what to render. This translates the functional description of the scene into a set of mutable updates for Babylon. This makes it fun and easy to describe what is rendered:

Flashlight.ml

let render model =
  let open React3d in
  let light =
    if model.isFlashlightOn then P.transform [ P.pointLight []; P.spotLight [] ]
    else P.transform []
  in
  P.transform ~position:(Vector3.zero ()) [ P.mesh flashLightMesh; light ]

Immutable Entity-Component-System

This project has a quick-and-dirty immutable entity-component-system, defined in EntityManager.mli

Entities are declarative and built by composing components:

let entity position =
  let open EntityManager.Entity in
  define (initial position)
  (* an update function, run every frame *)
  |> withThink tick
  (* a render component *)
  |> withReadonlyComponent Components.render render
  (* a grabbale component *)
  |> System_Grabbable.Entity.grabbable
       ~readGrabState:(fun { grabState; _ } -> grabState)
       ~writeGrabState:(fun grabState state -> { state with grabState })
       ~grabHandles
  (* a dynamic physics component, that changes staet when grabbed *)
  |> System_Physics.Entity.dynamic
       ~read:(fun { grabState; physicsState; _ } ->
         match grabState |> GrabState.state with
         | GrabState.Ungrabbed -> Some physicsState
         | GrabState.Grabbed _ -> None)
       ~write:(fun state entity ->
         match state with
         | None -> entity
         | Some state -> { entity with physicsState = state })

This was just a fun experiment; so I apologize that is not well documented.

License

The code in this repo, aside from external dependencies (Babylon/Ammo/Noise) is licenesed under the MIT License.

Summary

I enjoyed writing an app in this style, however, I found the the OCaml toolchain limited. Ultimately I wanted to cross-compile this to a native Android application, but I found the cross-compilation story for the OCaml toolchain to be quite lacking (compared to go or rust, for example). This motivated to pivot to Rust. I'd still like to revisit this functional style of gameplay logic, perhaps using a tool like Fable, which could target Rust and then be cross-compiled.

About

An experiment in building a WebXR experience with a functional programming language (OCaml)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages