MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MayaFlux: Getting Started

MayaFlux is a C++20/23 framework for real-time computation across sound, geometry, image, and network. All data is the same numerical substrate; the domain annotation decides where a buffer cycle goes, not what the data is.

This guide covers environment setup and the structure of a MayaFlux program. Tutorials that build on this foundation are at mayaflux.org/tutorials.


Weave: Recommended for Most Users

Weave is the installer and project tool for MayaFlux. Download it from the Weave releases page.

Installing MayaFlux

Launch Weave and choose Install MayaFlux. Weave downloads the framework, installs all required dependencies, and configures your environment. Restart your terminal when it finishes.

Creating a Project

Launch Weave and choose Create Project. Enter a name and pick a destination. Weave generates a ready-to-build project:

MyProject/
├── CMakeLists.txt
├── CMakePresets.json
├── community.cmake
├── cmake/
│ ├── mayaflux.cmake
│ ├── shaders.cmake
│ └── build_community.cmake
├── src/
│ ├── main.cpp
│ └── user_project.hpp
├── data/
│ └── shaders/
└── .gitignore

src/user_project.hpp is where you write your code. CMakeLists.txt is yours to edit; the MayaFlux integration lives in cmake/mayaflux.cmake and is included automatically.

CMakePresets.json ships with every project and defines debug and release presets. CLion, VS Code with the CMake extension, and Visual Studio all pick up the presets automatically when you open the project folder.

cmake --preset release
cmake --build --preset release
./build/MyProject

On Windows the binary lands in build\MyProject.exe.

Live Coding (Lila)

Check Enable Live Coding (Lila) in the project creation dialog to embed the Lila JIT compiler in your process. Connect LilaCode (VS Code) or lila.nvim (Neovim) to evaluate C++ against your running application in real time.

Community Modules

Community modules are C++ source libraries that compile directly into your project with no plugin boundary. In Weave, open your project and choose Add Community Module. Weave fetches the community registry, checks version compatibility, clones the module into community/<name>/, and registers it in community.cmake. Rebuild after adding modules.

After Weave completes setup, jump to Program Structure.


Building MayaFlux Itself From Source

Weave is the supported path for using MayaFlux; it downloads a prebuilt framework and generates a project against it, no compiler toolchain or dependency management on your end.

Building the framework itself from source is a different, separate task, for contributors modifying the engine, not for writing programs against it. If that's what you're doing, see `docs/Dev_Getting_Started.md` in the MayaFlux repository, which covers dependencies, setup scripts, build presets, and the in-tree run loop.

If you're not sure which you need: if you want to write music, visuals, or interactive pieces with MayaFlux, use Weave. If you want to change how MayaFlux itself works, build from source.


Program Structure

A MayaFlux program has two entry points defined in src/user_project.hpp:

#pragma once
#define MAYASIMPLE
void settings() {
// Runs before the engine starts.
// Configure sample rate, buffer size, channels, logging.
stream.sample_rate = 48000;
stream.buffer_size = 256;
stream.output.channels = 2;
}
void compose() {
// Runs after the engine starts.
// All computation, routing, scheduling, and rendering lives here.
}
Core::GlobalStreamInfo stream
Definition Config.cpp:36
Core::GlobalStreamInfo & get_global_stream_info()
Gets the stream configuration from the default engine.
Definition Config.cpp:62

main.cpp calls Init(), settings(), Start(), compose(), Await(), and End() in order. You do not edit it unless you need custom engine configuration beyond what settings() exposes.

MAYASIMPLE

Defining MAYASIMPLE before including MayaFlux.hpp pulls in the full concrete type set and brings all MayaFlux namespaces into scope. Without it you get the API surface only. User projects built via Weave define it by default.

The <tt>vega</tt> global

vega is the global Creator instance. It is the factory for all computation objects: generators, filters, networks, buffers, containers, mesh loaders, input nodes. Every object created through vega is registered with the engine automatically.

auto sine = vega.Sine(440.0, 0.5) | Audio[0];
auto modal = vega.ModalNetwork(12, 220.0) | Audio[{0, 1}];
auto audio = vega.read_audio("res/drum.wav") | Audio;
auto img = vega.read_image("res/texture.png") | Graphics;
auto mesh = vega.read_mesh_network("res/scene.glb");
auto Sine(Args &&... args) -> std::shared_ptr< MayaFlux::Nodes::Generator::Sine >
Definition Creator.hpp:115
auto read_audio(const std::string &filepath) -> std::shared_ptr< Kakshya::SoundFileContainer >
Definition Creator.hpp:142
auto ModalNetwork(Args &&... args) -> std::shared_ptr< MayaFlux::Nodes::Network::ModalNetwork >
Definition Creator.hpp:127
auto read_mesh_network(const std::string &filepath, IO::TextureResolver resolver=nullptr) -> std::shared_ptr< Nodes::Network::MeshNetwork >
Definition Creator.hpp:182
auto read_image(const std::string &filepath) -> std::shared_ptr< Buffers::TextureBuffer >
Definition Creator.hpp:157

The | Audio and | Graphics operators are domain annotations. They attach a processing token that decides which subsystem drives the object and at what rate. They do not change what the object is.


A First Program

The example below is complete and runnable. It loads a texture, generates a parametric surface, and animates it frame by frame via a coroutine inside a Nexus entity. A movable light agent influences all render processors simultaneously. Keyboard input moves the light.

#pragma once
#define MAYASIMPLE
void settings()
{
stream.sample_rate = 48000;
stream.buffer_size = 256;
stream.output.channels = 2;
};
void compose()
{
auto window = MayaFlux::create_window({ "surface", 1920, 1080 });
auto view = []() -> Kinesis::ViewTransform {
return Kinesis::look_at_perspective(
glm::vec3(0.0F, 2.0F, 5.0F),
glm::vec3(0.0F),
glm::radians(45.0F), 1920.0F / 1080.0F, 0.1F, 100.0F);
}
// Supply your own image relative to the base directory of the project
auto img = vega.read_image("res/texture.png") | Graphics;
// Parametric surface: initial geometry
auto surf_fn = [](float u, float v, float t) -> glm::vec3 {
float a = u * glm::two_pi<float>();
float b = v * glm::two_pi<float>();
return {
std::sin(a + t) * (1.0F + 0.3F * std::cos(3.0F * b)),
std::sin(2.0F * a - t) * std::sin(b) * 0.5F,
std::cos(a + t) * (1.0F + 0.3F * std::cos(3.0F * b)),
};
};
auto data = Kinesis::generate_parametric_surface(
[&](float u, float v) { return surf_fn(u, v, 0.0F); }, 64, 64);
auto surf_node = std::make_shared<MeshWriterNode>(data.vertex_count()) | Graphics;
surf_node->set_mesh(data);
auto surf_buf = vega.GeometryBuffer(surf_node) | Graphics;
surf_buf->set_texture(img->get_gpu_texture());
surf_buf->setup_rendering({
.target_window = window,
.fragment_shader = "mesh_textured_lit.frag.spv",
});
surf_buf->get_render_processor()->set_view_transform_source(view);
// Light agent: influences the surface render processor
auto& light_pos = make_persistent<glm::vec3>(0.0F, 1.5F, 0.0F);
constexpr glm::vec3 half { 0.04F, 0.04F, 0.04F };
auto light = std::make_shared<Nexus::Agent>(
0.0F,
[](const Nexus::PerceptionContext&) { },
[](const Nexus::InfluenceContext&) { });
light->set_position(light_pos);
light->set_color(glm::vec3(1.0F, 0.9F, 0.7F));
light->set_intensity(1.5F);
light->set_radius(4.0F);
light->render(mgr, { .target_window = window, .topology = Portal::Graphics::PrimitiveTopology::LINE_LIST });
light->get_render_processor(window)->set_view_transform_source(view);
light->set_vertices<LineVertex>(Kakshya::to_line_vertices(
Kinesis::cuboid_wireframe(light_pos, half, glm::vec3(1.0F, 0.9F, 0.7F))));
light->add_influence_target(surf_buf->get_render_processor());
auto fabric = make_persistent_shared<Nexus::Fabric>(
fabric->wire(light)
.every(1.0 / 60.0, Vruta::ProcessingToken::FRAME_ACCURATE)
.finalise();
// Surface animation: coroutine updates mesh geometry each frame
auto st = make_persistent(0.F);
auto surf_driver = std::make_shared<Nexus::Emitter>(
[surf_node, surf_fn, &st](const Nexus::InfluenceContext&) {
auto d = Kinesis::generate_parametric_surface(
[&](float u, float v) { return surf_fn(u, v, st); }, 64, 64);
const auto& vb = std::get<std::vector<uint8_t>>(d.vertex_variant);
surf_node->set_mesh_vertices(
std::span(reinterpret_cast<const MeshVertex*>(vb.data()), d.vertex_count()));
st += 0.01F;
});
fabric->wire(surf_driver)
.every(1.0 / 60.0, Vruta::ProcessingToken::FRAME_ACCURATE)
.finalise();
// Keyboard: move the light
static constexpr float step = 0.05F;
auto move = [light, half, &light_pos, &mgr](glm::vec3 delta) {
light_pos += delta;
light->set_position(light_pos);
light->set_vertices<LineVertex>(Kakshya::to_line_vertices(
Kinesis::cuboid_wireframe(light_pos, half, glm::vec3(1.0F, 0.9F, 0.7F))));
};
auto mk_mover = [&](glm::vec3 d) {
return std::make_shared<Nexus::Emitter>(
[move, d](const Nexus::InfluenceContext&) { move(d); });
};
fabric->wire(mk_mover({ step, 0, 0 })).on(window, IO::Keys::D, true).finalise();
fabric->wire(mk_mover({ -step, 0, 0 })).on(window, IO::Keys::A, true).finalise();
fabric->wire(mk_mover({ 0, step, 0 })).on(window, IO::Keys::W, true).finalise();
fabric->wire(mk_mover({ 0, -step, 0 })).on(window, IO::Keys::S, true).finalise();
fabric->wire(mk_mover({ 0, 0, step })).on(window, IO::Keys::Q, true).finalise();
fabric->wire(mk_mover({ 0, 0, -step })).on(window, IO::Keys::E, true).finalise();
window->show();
};
size_t a
size_t b
auto GeometryBuffer(Args &&... args) -> std::shared_ptr< MayaFlux::Buffers::GeometryBuffer >
Definition Creator.hpp:139
Kakshya::LineVertex LineVertex
Definition VertexSpec.hpp:8
Kakshya::MeshVertex MeshVertex
Definition VertexSpec.hpp:9
std::shared_ptr< Vruta::EventManager > get_event_manager()
Gets the event manager from the default engine.
Definition Chronie.cpp:27
Creator vega
Global Creator instance.
Definition Creator.cpp:23
std::shared_ptr< Core::Window > create_window(const Core::WindowCreateInfo &create_info)
Create a new window with specified parameters.
Definition Windowing.cpp:14
T & make_persistent(Args &&... args)
Construct a T in place, retain it for process lifetime, and return a direct reference.
Definition Persist.hpp:90
std::shared_ptr< Buffers::BufferManager > get_buffer_manager()
Gets the buffer manager from the default engine.
Definition Graph.cpp:132
std::shared_ptr< Vruta::TaskScheduler > get_scheduler()
Gets the task scheduler from the default engine.
Definition Chronie.cpp:22

What this shows:

  • vega.read_image and vega.GeometryBuffer are factory calls; domain annotation follows at the call site
  • A GraphicsRoutine coroutine inside a Nexus::Emitter owns its animation loop, suspended one frame at a time via FrameDelay
  • A Nexus::Agent acting as a light registers influence targets directly on render processors; moving the light position updates all of them simultaneously
  • Keyboard input wires through Wiring::on(key, held) - each key is its own entity, each entity's influence function applies the delta

What to Read Next


FAQ

macOS: do I need Homebrew LLVM?

No. MayaFlux compiles with Apple Clang from Xcode Command Line Tools. Homebrew LLVM is not used for compilation. LLVM is required as a runtime dependency for Lila (the JIT environment) and is installed by the setup script, but it is not your compiler. This applies to building MayaFlux itself from source; see `docs/Dev_Getting_Started.md`.

What is the minimum OS version?

Weave-installed MayaFlux (currently 0.4.1) targets: Windows 10 version 1909+, Fedora 43, Ubuntu 25.10, macOS 15.

Building from source targets a newer floor, since source currently tracks 0.5-dev: Windows 10 version 1909+, Fedora 44, Ubuntu 26.04 LTS, macOS 26 (Tahoe). See `docs/Dev_Getting_Started.md` for the from-source requirement list.

The Windows floor is set by MSVC 2022's own minimum supported OS. Win32 windowing, WinMM, and WASAPI all predate this by a wide margin and impose no additional constraint.

What happens if a dependency is missing?

This applies to building MayaFlux itself from source. Weave-installed MayaFlux handles dependencies for you. See `docs/Dev_Getting_Started.md` for the from-source dependency list and setup scripts.

Can I use MayaFlux without the JIT (Lila)?

Yes. Lila is part of the engine but you do not have to use it. Programs written entirely in compose() and compiled normally do not touch the JIT path. Weave's live coding toggle controls whether Lila is embedded in your project at all.

Where does my code go?

In src/user_project.hpp. It is never overwritten by Weave updates. main.cpp is the engine entry point and should not be modified unless you need a custom startup sequence.