I wanted to share a project I’ve been working on, which began with my exploration of Babylon.js. As I learned more about it, I was amazed by the power and architectural approach that Babylon.js offers. The deeper I delved into its internals, the more I aimed to simplify development by implementing a component-based approach, declarative syntax, and an abstraction layer to manage the lifecycle of Babylon.js entities. In my search for an existing similar solution, I came across the fantastic work done by the creators of react-babylonjs. While that solution is excellent, I wanted to create my own framework, not only to gain a deeper understanding of Babylon.js and React, but also to create something more opinionated and better suited to my specific use case.
Here are the key features of the framework, Reactylon:
Custom React Reconciler: it creates an abstraction layer that simplifies the creation and management of Babylon.js components using JSX syntax. It handles updates, removals, and complex relationships between entities such as meshes, materials, textures, and GUI elements.
Full TypeScript support: leveraging Babylon.js classes, the appropriate props for each Babylon.js entity are generated and utilized within their corresponding React components.
Documentation: it covers the core concepts of Babylon.js, with interactive examples and code snippets to illustrate how things work. Each section includes links to the official Babylon.js documentation for further exploration.
CLI for starters: it simplifies the process of generating new Babylon.js and Babylon Native projects, making it easier to start in different environments.
You can find the framework and its documentation here.
I want to take a moment to express my gratitude to the Babylon.js community. Resources like the forum, the documentation, and the playground played a pivotal role in helping me bring this project to life. I hope my work can be useful to others in the community, and I’d love to hear your thoughts and feedback.
Thank you so much! I’m fully tuned in to fix anything that comes up and always happy to hear any thoughts or ideas you have while exploring the framework. Feel free to reach out anytime!
I wanted to share some updates for new version 1.1.0:
Custom loading screen
Pass a custom React component as loader prop of Engine component to personalize the loading screen.
Multiple canvases
- Single scene: supports n canvases with 1 corresponding scene.
- Multiple scenes: supports n canvases, each with its own corresponding scene.
You can find both of these features in the “Advanced” section of the documentation.
Um. Thanks for the Christmas present @SimoneDev ! Super great docs, and simple API, clearly you are very talented. I will be playing with this bigtime.
I’ve added over 100 code sandboxes to the documentation. Rather than just displaying static code and interactive examples, the goal is to enable immediate feedback and the ability to modify code, run it, and observe results in real-time in an isolated environment.
They can provide valuable insights for custom Webpack/TypeScript configurations, dependency injection, assets loading, environment variables, and can serve as golden paths not only for Reactylon but also for Babylon.js when used with a module bundler!
Next to each example, you’ll find the “Open in Stackblitz” button, which links directly to the sandbox.
For future reference, I’m sharing all the sandboxes, grouped by feature, here:
However, please note that the code sandboxes currently do not support tree-shaking for the Babylon.js modules. This is due to Reactylon’s current lack of support for it. If you’re curious about the issue, there’s an interesting solo discussion in the GitHub issue: Enable tree shaking.
Thank you for your kind words. It truly means a lot for me. I’m incredibly grateful for your offer to help. At this stage, the best support you could offer would be feedback and suggestions for improvement or new features. Your insights would be incredibly valuable.
Thanks again, and I look forward to hearing your thoughts!
Thank you! The different casing conventions serve to differentiate Babylon.js components from regular React components:
Babylon.js components (written in camelCase) belong to the secondary renderer (Reactylon).
Regular React components (written in PascalCase) belong to the primary renderer (React).
Babylon.js components are JSX intrinsic elements, similar to standard HTML elements like <div> or <a>. Their lifecycle is fully managed by the secondary renderer, which handles all Babylon.js-specific operations (such as instantiation, managing parent-child relationships, updating, disposal, etc…).
The expressive power an interface like this gives the developer is incredible. I know that the tree structure of a complex object such as a dragon or a john deer tractor would be probably large, but totally worth it imo if I can easily manipulate the mesh node tree of complex 3d assets and switch between animation states without sacrificing any performance.
I am 99% sold, but I have to ask; how does this perform at scale? Let’s say I have a scene composed of a few hundred elements, does the compilation time take more than 15 seconds? Does the framework itself introduce a lot of overhead when managing many elements?
For future roadmaps, do you intend to integrate the havok physics engine in a manner similar to rapier? They provide a similar xml api:
I’ve never used it myself as it always seemed a bit coarse and generic, but I’d imagine you could find an elegant way to expose the havok physics engine in a similar manner given what you already have.
This is so impressive, fantastic work!
EDIT: You added xml support for the babylon.js GUI!!! I can’t stress enough how much boiler plate code this saves.
EDIT 2: How you pronounce the framework? Is it react - e - lon?
I am 99% sold, but I have to ask; how does this perform at scale? Let’s say I have a scene composed of a few hundred elements, does the compilation time take more than 15 seconds? Does the framework itself introduce a lot of overhead when managing many elements?
The framework itself shouldn’t introduce much complexity because its main job is to manage the lifecycle of the Babylon.js components. It tries to optimize when it is possible (removeChild method for instance). The critical point is the prepareUpdate method, which decides whether an update is necessary or not. Of course, a lot of fine-tuning can be done here mainly using Babylon.js utilities (e.g., equality checks between vectors) and this is a work in progress!
The time it takes to compile the scene will depend more on the complexity of the assets and the number of elements. However, this can vary based on your specific setup (e.g., shaders, textures, meshes, etc.). Performance optimizations like lazy loading, scene partitioning and, in general, Babylon.js optimization (Optimize Your Scene) are game changers.
For future roadmaps, do you intend to integrate the havok physics engine in a manner similar to rapier?
Yes, the idea is to integrate Havok with less configuration required from the developer and to augment the meshes with PhysicsAggregateParameters leaving the task of binding to Reactylon.
You added xml support for the babylon.js GUI!!! I can’t stress enough how much boiler plate code this saves.
Also here, the goal is to reduce the boilerplate and leave the task of adding and removing the controls to Reactylon.
How you pronounce the framework? Is it react - e - lon?
So if I’m understanding you correctly, the various steps a babylon.js developer can take to optimize their scene are still the biggest factors.
The framework itself shouldn’t introduce much complexity because its main job is to manage the lifecycle of the Babylon.js components. It tries to optimize when it is possible (removeChild method for instance). The critical point is the prepareUpdate method, which decides whether an update is necessary or not.
Let’s say the 3d scene in question is composed of hundreds of destructible rocks with varying geometry, as well as 60+ destructible trees instanced from 3 different models (roughly 20 instances for each tree type variation) and the user is free to move the camera anywhere throughout the scene. In this case, what is the reactylon framework is considering exactly? Frustum view culling logic? Application logic (is the rock or tree destroyed)? My concern would be if there’s any heavy math calculations that need to be done per frame per scene entity.
If not, then your framework seems quite viable for managing large open world scenes, which opens it up for use in a gaming context.
So if I’m understanding you correctly, the various steps a babylon.js developer can take to optimize their scene are still the biggest factors.
Absolutely! Reactylon acts as a pure renderer, empowering you with React’s features like a component-based approach, hooks, context, and reduced boilerplate through JSX syntax. At the same time, it serves as a Babylon.js facilitator, managing tasks like automatic parent-child relationships, scene injection, and lifecycle management.
Let’s say the 3d scene in question is composed of hundreds of destructible rocks with varying geometry, as well as 60+ destructible trees instanced from 3 different models (roughly 20 instances for each tree type variation) and the user is free to move the camera anywhere throughout the scene. In this case, what is the reactylon framework is considering exactly? Frustum view culling logic? Application logic (is the rock or tree destroyed)? My concern would be if there’s any heavy math calculations that need to be done per frame per scene entity.
Reactylon doesn’t handle rendering logic directly; instead, it delegates all of that to Babylon.js (fortunately! ). In this case, Reactylon won’t make any decisions for you, it’s up to you to choose whether to use instances (instanceFrom prop), clones (cloneFrom prop), level of detail, or other optimization techniques that fit your use case.
In my rocks & trees example, perhaps a state update triggers (in game example, the user now has x-ray glasses equipped and can see through stones), the material property being passed via a reactylon component props argument now includes an alpha value of 0.2 instead of 1.0, what ever consequences that would have on deciding how many rock instances to render, it’s not something your framework would influence, right?
However, with parent child relationships being managed by reactylon what is the preferred way to remove a single branch from a tree model that has, say 10 branches? Should we be using conditional rendering with the reactylon component argument props for each individual tree branch? Or is that state better handled via uniforms that dictate at the shader level what geometry to render? If the former, this get’s quite complicated when you have over 5 trees in the scene, right? Could we possibly encapsulate this logic in a reactylon component and have it applied to the many individual instances of that same tree? This would be a really powerful feature imo.
To be clear, I’m not using any of this as an argument against using reactylon, I just want to know what are the ways I should be using it that make the most sense. It’s me commenting on your module import tree-shaking issue btw I’m very interested in seeing this take off!
In my rocks & trees example, perhaps a state update triggers (in game example, the user now has x-ray glasses equipped and can see through stones), the material property being passed via a reactylon component props argument now includes an alpha value of 0.2 instead of 1.0, what ever consequences that would have on deciding how many rock instances to render, it’s not something your framework would influence, right?
Correct! Reactylon itself wouldn’t influence how many rock instances to render based on the alpha value provided. The framework is primarily concerned with propagating the state update (in this case, the new alpha value) and ensuring that the components reflect the updated state.
However, with parent child relationships being managed by reactylon what is the preferred way to remove a single branch from a tree model that has, say 10 branches? Should we be using conditional rendering with the reactylon component argument props for each individual tree branch? Or is that state better handled via uniforms that dictate at the shader level what geometry to render? If the former, this get’s quite complicated when you have over 5 trees in the scene, right? Could we possibly encapsulate this logic in a reactylon component and have it applied to the many individual instances of that same tree? This would be a really powerful feature imo.
To simplify the management of multiple branches, you can encapsulate the branch-rendering logic in a React component and pass the props to the inner Reactylon components. I’ve created a small code sandbox where a React component (Content) manages the logic for adding, removing, and hiding ten boxes (Reactylon components).
Under the hood:
when you remove a box, you are calling .dispose()
when you add a box, you are calling MeshBuilder.CreateBox
when you change the visibility, you are modifying mesh.isVisible attribute (this is the reason why you don’t see the box in the scene but it still counts as mesh on bottom-left text)
when you reset, you are not recreating all boxes but you add adding (if your boxes are < 10) or you are removing (if your boxes are > 10) the others.
Currently, I’m working on reactivity. Please mind that the above example works only in the sandbox using the 1.2.0-alpha.1 version, the official 1.2.0 version will be released in the coming days.
The fact that you setup that demo with 125 lines of code (including the export statement :p) is so awesome and refreshing.
Perhaps you’ve already exposed this sort of interface/api, but is there a way to create many instances of the same mesh (they share the same geometry, and optionally share the same materials), and can individually update their show/hide state?
Going off the example you provided, lets say your group of boxes constitutes a single mesh tree. I want to create many instances that all respond to the same hide/show logic at an individual level. That is each group of boxes can be toggled on/off, and each group of boxes can hide/show individual boxes.
The goal I’m perusing here is to be able to use very efficient rendering that minimizes draw calls, but updates to each instance can be done/managed from a higher level place such as your reactylon component props.
In theory this would look something like this:
const boxes = new Array(10 * 1000);
const hideBoxState = new Array(10 * 1000).fill(true);
for (let index=0; index <10 * 1000; index++) {
boxes[index] = (
<box
/* some sort of prop to indicate that this is merely an instance */
key={`box-${index}`}
name={`box-${index}`}
options={{ size: 0.1 }}
isVisible={hideBoxState[index]}
positionX={-2.5 + 0.5 * (index % 10)}
positionZ={-2.5 + 0.5 * (index % 1000)}
/>
);
}
And then our game logic would control which boxes are being shown depending on things like frustum culling or boxes that have been destroyed. My main concern is not overloading the react renderer with large numbers of instances in this manner.