It's great... except that it turns out to be a performance pitfall if the state needs to be frequently updated. The React Three Fiber docs mention this here:
To fix this, they suggest using useRef and manually updating the object. But then your code ends up just as complicated, if not more so, than if you had just stuck with vanilla three.js.
Yeah, I can only imagine what removing an element from the middle of a thousand-element array of entities would do. This has always been a performance weak point for React, but React trees aren’t typically rebuilt from the root 60 times a second. With this, they are.
I like react because it gives you escape hatches like this. You can take a range of approaches, starting with simple and declarative, only moving to gross and performant if required.
Sadly I’ve lost the URL but there was once a great comparison of Svelte vs React for this exact functionality. The performance was orders of magnitude better IIRC.
React code looks great, the dev experience is fantastic. But user experience sometimes less so. Hooks kind of hide what’s going on.
I am exploring this area in a pet project, because some part of me has always been absolutely confident that we can do better than both vdom and custom compilers. So far, I have made really exciting progress, though it has never been more than 2-3 levels deep, for example a basic todo list, so it's hard to say that it will solve the main difficulty elegantly, but so far it seems to do so in principle, and I hope soon to come up with a use-case that allows me to finally create the deepRef function that I can just tell is the final piece of the puzzle.
Minor nit, because this is one of my weird special interests: this (being render-agnostic) isn’t necessarily a property of React’s virtual DOM. It’s a property of JSX being explicitly specified without semantics.
For instance, Solid also supports custom JSX renderer, but doesn’t use a VDOM to achieve that.
I find it helpful to think of JSX as analogous to a macro (in the lispy sense of the term), except that its syntax and implementation are decoupled. Most compiler tooling assumes the implementation maps to a function call, but even that isn’t strictly required (and again, Solid is a counter example).
JSX gets compiled to a tree of function calls, but the hard part is done using react (or solid, or svelte, or ...) to hook in to the lifecycle of nodes in that tree.
Something I wish is that you could "render" JSX agnostically and just get a JSON data structure describing a tree of tags, which is not specific to the platform or even the framework, and then you could pass that off to whichever subsystem for whichever purpose (or even serialize it or send it over a wire). We wouldn't need separate build plugins for each JSX-using framework, you'd just pass them your data
Hello! One of the main challenges of figuring a JSX syntax out is what to do about the “children” prop.
Experientially, typescript still has a bit of trouble figuring out the right types for these nested structures. Even with a typed jsx function it sometimes doesn’t infer correctly, so providing plugin capability would take a very careful hand
Right, but only with a custom transpilation step. It would be nice if there were only a single standard JSX transpilation that all the frameworks and platforms ingested the output of
It kinda is! The only difference between JSX transpiler outputs is the factory function (and the Fragment component). For React the factory function it's `React.createElement`, for Preact it's `h`.
Babel has a pragma property, and esbuild allows you to pass it in the command line: `--jsx-factory=h`.
The point is about the declarative abstraction and how it's useful for people who don't know what "React Three" might entail, not to enumerate all the frameworks that have it under a submission about "React Three Ecosystem".
Yeh that seems problematic because you no longer have access to the render loop, you are letting React manage your render loop which seems kinda problematic for a game where you want very granular control over what happens each iteration, fps, etc.
This imperative/declarative shift happened as part of UIKit -> SwiftUI too. However, it’s still pretty well accepted that if you’re doing anything “complicated” you’re going to end up writing UIKit imperative code.
I wonder if there’s any research (PL or maybe even more philosophical) on whether declarative approaches can logically cover all imperative use cases.
Basically: is there something special about “verbs” (imperative) that let you do things that “nouns” (declarative) cannot, at least within reasonable verbosity constraints?
Yeah, one of the qualities of the abstraction is how often you have to reach for the escape hatch, how nice it is to use the escape hatch, and whether it's an escape hatch vs. just two clean modes of tooling working together.
SwiftUI has failed pretty much all of those for me.
In react-pixi / react-three, you have your declarative component tree alongside a `useTick`/`useFrame` function that you can use to do more surgical work, and it works well together. Each component can tap into the tick to update itself.
I've migrated a few medium-sized pixi/three projects to their React wrappers, and the code cleaned up so well that I could work on them again.
Before that, I'd tried modeling games in Elm and then using its port system to send the game state to JS which then updates the pixi/three world. But it's exactly this updateWorld(oldWorld, newState) that's hard to write, yet that's what these libs do for you.
Well, you can interpret the program's text as a declarative (and completely static) approach to describe what will happen at runtime.
But you can actually see this dichotomy in template libraries, they usually have constructs making them fully Turing-complete (loops, conditionals, etc).
This declarative system often is less intuitive once you get into the nitty gritty of things imo. Showing the hello world most basic example proves nothing
It's worse IMO. In a real three.js example, to repo their example, you'd make one BoxGeometry and 2 Mesh(es) But that wouldn't be idomatic React so they don't show it.
Are there any guides on using the "react style" framework (JSX / State) to generate outputs other than DOM? I've been interested in the idea of doing something similar for JSON or markdown document generation.
note three.js [1] has nothing to do with React out of the box though, this page highlights an atypical way of using three.js through a popular React binding.
You might be getting downvoted for saying it's an "atypical" way of using three.js. the pmnd.rs community (for example) is quite large.
I understand why people like the declarative nature of react three fiber, but it's quite unfortunate that it requires something like code sandbox to allow modification / working with it on the web- but that's by nature due to being react.
Vanilla three.js can be written surprisingly similarly, if you are disciplined about breaking things up into functions/components. And no react necessary / can embed a code editor and allow direct modification.
For what it’s worth, this is also true of whatever else one might express with JSX: imperative code (and syntax, and semantics) can be structured in a way that closely resembles declarative code… with discipline.
It’s doesn’t have to be especially onerous discipline if you embrace it, but it becomes considerably more onerous as it becomes more social: if some members of a team/contributors to a project embrace it more/less than others, that difference in commitment becomes a constant source of friction.
> It’s doesn’t have to be especially onerous discipline if you embrace it, but it becomes considerably more onerous as it becomes more social: if some members of a team/contributors to a project embrace it more/less than others, that difference in commitment becomes a constant source of friction.
That's one of the stronger arguments for opinionated pre-processors/frameworks/libraries like Typescript/TSX/JSX/React in general. Because it abstract away those things that only some team members would embrace, you effectively make everyone embrace them. That leads to less friction.
But this reduced friction comes at a cost: more complex abstractions and incidental bugs related to that complexity. And as far as the procedural vs declarative: after a certain degree of complexity, I find myself introducing procedural codes within useEffects, useMemos anyways
I start using X3D at work in order to enable some cool functionality showing steps to build an assembly in 3D, and it's actually insane how high the performance is. It's all HTML components, so I wrote a super thin React wrapper (essentially the scene needs to be manually loaded once, that's about all) and have been cruising with that.
Effortless to fly, rotate, hide or highlight parts, decompose things into x3d assets, and so on.
It might have issues somewhere, I haven't widely tested browser compat or anything, but it's curious to me how little I see it versus how much I see ThreeJS when it seems that X3D is the more capable platform.
been there - love how clean react-three looks at first but man, once you hit real perf walls, its like, why am i even using this lol. you think the push for these declarative libs is just for quicker onboarding or something deeper?
I tried to build a game with react expo and react three last year and it was painful to me.
There were version mismatches and some features didn't work if I combined them in some ways that I needed sooner or later when trying to finish a project.
I believe it is correlated with the fact I tried this on mobile, but maybe someone else has made the same experience with it.
At this point I start to evaluate technologies end to end to not be surprised when projects break at a certain level of maturity or in size when they get close to release in terms of feature completeness.
Yeah, they went all in on Codesandbox early on, and at the time that was a good idea.
But very soon after Codesandbox made the switch to containers and started chasing monetization. I saw an interaction between the creators of each on Twitter at the time where Codesandbox promised not to throttle or limit R3Fs examples. But I don't think they fully kept that promise.
Changing priorities I guess. I forget the name of the guy who originally created R3F but he is really skilled at making beautiful examples. I think he's much less involved now - the people I see responding to PMNDRS GitHub issues these days are focused on the technical stuff rather than docs and examples.
I'm guessing that Safari on iOS doesn't support any of the necessary webgl here? Every example shows up with a blank component with a question mark emoji at the center.
React's threejs/pixijs bindings are a great example of what vdom diffing can let you do.
Since they bring their own reconcile(a, b, diff) function to React, these libs can turn:
Into: In other words, you write declarative code, and it does the hard imperative work of adding/removing/disposing/updating things for you.Just like how it helps you sync your data model to underlying stateful DOM nodes; the idea can be applied on top of any stateful system.
It's great... except that it turns out to be a performance pitfall if the state needs to be frequently updated. The React Three Fiber docs mention this here:
https://r3f.docs.pmnd.rs/advanced/pitfalls#avoid-setstate-in...
To fix this, they suggest using useRef and manually updating the object. But then your code ends up just as complicated, if not more so, than if you had just stuck with vanilla three.js.
Yeah, I can only imagine what removing an element from the middle of a thousand-element array of entities would do. This has always been a performance weak point for React, but React trees aren’t typically rebuilt from the root 60 times a second. With this, they are.
I like react because it gives you escape hatches like this. You can take a range of approaches, starting with simple and declarative, only moving to gross and performant if required.
Sadly I’ve lost the URL but there was once a great comparison of Svelte vs React for this exact functionality. The performance was orders of magnitude better IIRC.
React code looks great, the dev experience is fantastic. But user experience sometimes less so. Hooks kind of hide what’s going on.
I am exploring this area in a pet project, because some part of me has always been absolutely confident that we can do better than both vdom and custom compilers. So far, I have made really exciting progress, though it has never been more than 2-3 levels deep, for example a basic todo list, so it's hard to say that it will solve the main difficulty elegantly, but so far it seems to do so in principle, and I hope soon to come up with a use-case that allows me to finally create the deepRef function that I can just tell is the final piece of the puzzle.
The only way to have performant rendering in a React app is to eject from React's rendering pipeline — that is, to not use it at all.
State should basically only be used as "keyframes"
Minor nit, because this is one of my weird special interests: this (being render-agnostic) isn’t necessarily a property of React’s virtual DOM. It’s a property of JSX being explicitly specified without semantics.
For instance, Solid also supports custom JSX renderer, but doesn’t use a VDOM to achieve that.
I find it helpful to think of JSX as analogous to a macro (in the lispy sense of the term), except that its syntax and implementation are decoupled. Most compiler tooling assumes the implementation maps to a function call, but even that isn’t strictly required (and again, Solid is a counter example).
JSX gets compiled to a tree of function calls, but the hard part is done using react (or solid, or svelte, or ...) to hook in to the lifecycle of nodes in that tree.
Something I wish is that you could "render" JSX agnostically and just get a JSON data structure describing a tree of tags, which is not specific to the platform or even the framework, and then you could pass that off to whichever subsystem for whichever purpose (or even serialize it or send it over a wire). We wouldn't need separate build plugins for each JSX-using framework, you'd just pass them your data
Hello! One of the main challenges of figuring a JSX syntax out is what to do about the “children” prop.
Experientially, typescript still has a bit of trouble figuring out the right types for these nested structures. Even with a typed jsx function it sometimes doesn’t infer correctly, so providing plugin capability would take a very careful hand
You can build your own jsx renderer and get exactly that, it's not that difficult.
Right, but only with a custom transpilation step. It would be nice if there were only a single standard JSX transpilation that all the frameworks and platforms ingested the output of
It kinda is! The only difference between JSX transpiler outputs is the factory function (and the Fragment component). For React the factory function it's `React.createElement`, for Preact it's `h`.
Babel has a pragma property, and esbuild allows you to pass it in the command line: `--jsx-factory=h`.
https://esbuild.github.io/content-types/#auto-import-for-jsx
I knew it transpiled to a function call, though I didn't know babel lets you parameterize that without writing a custom plugin
JSX parameters aren’t necessarily serialisable though?
And JSX is not needed either, as seen with Threlte.
https://threlte.xyz/
When all you know is React, everything gets viewed through that limited lens.
Sheesh.
The point is about the declarative abstraction and how it's useful for people who don't know what "React Three" might entail, not to enumerate all the frameworks that have it under a submission about "React Three Ecosystem".
Yeh that seems problematic because you no longer have access to the render loop, you are letting React manage your render loop which seems kinda problematic for a game where you want very granular control over what happens each iteration, fps, etc.
This imperative/declarative shift happened as part of UIKit -> SwiftUI too. However, it’s still pretty well accepted that if you’re doing anything “complicated” you’re going to end up writing UIKit imperative code.
I wonder if there’s any research (PL or maybe even more philosophical) on whether declarative approaches can logically cover all imperative use cases.
Basically: is there something special about “verbs” (imperative) that let you do things that “nouns” (declarative) cannot, at least within reasonable verbosity constraints?
Yeah, one of the qualities of the abstraction is how often you have to reach for the escape hatch, how nice it is to use the escape hatch, and whether it's an escape hatch vs. just two clean modes of tooling working together.
SwiftUI has failed pretty much all of those for me.
In react-pixi / react-three, you have your declarative component tree alongside a `useTick`/`useFrame` function that you can use to do more surgical work, and it works well together. Each component can tap into the tick to update itself.
I've migrated a few medium-sized pixi/three projects to their React wrappers, and the code cleaned up so well that I could work on them again.Before that, I'd tried modeling games in Elm and then using its port system to send the game state to JS which then updates the pixi/three world. But it's exactly this updateWorld(oldWorld, newState) that's hard to write, yet that's what these libs do for you.
Well, you can interpret the program's text as a declarative (and completely static) approach to describe what will happen at runtime.
But you can actually see this dichotomy in template libraries, they usually have constructs making them fully Turing-complete (loops, conditionals, etc).
Aren’t declarative approaches just abstractions over imperative? Somehow you need to get there. (Processor executes instructions, in the end)
I think there are declarative languages that are Turning-complete, like Prolog, etc. So declarative approaches should be equally good.
A (the?) major area where this is generally considered unanswered, and active in PL research, is “effects” (as in, side-effects).
This declarative system often is less intuitive once you get into the nitty gritty of things imo. Showing the hello world most basic example proves nothing
It's worse IMO. In a real three.js example, to repo their example, you'd make one BoxGeometry and 2 Mesh(es) But that wouldn't be idomatic React so they don't show it.
Just to clarify, you'd write the bottom code (declarative) and the library translates it to the above (imperative) code?
That’s right
Are there any guides on using the "react style" framework (JSX / State) to generate outputs other than DOM? I've been interested in the idea of doing something similar for JSON or markdown document generation.
I don't have a good guide for you, but this is the lib you use to create custom React renderers:
https://www.npmjs.com/package/react-reconciler
I’m working on the documentation, but I have a useful implementation of <GithubActions > JSX:
https://github.com/levicape/fourtwo
The best way to use template JSX is either with a CLI, or by using #! pragma to output the constructed yaml after using a builder.
It's called Maslow's hammer.
If the only tool you have is React everything around you start looking remarkably similar to JSX.
note three.js [1] has nothing to do with React out of the box though, this page highlights an atypical way of using three.js through a popular React binding.
[1] https://threejs.org/
Exactly, as seen with Threlte as a counterpoint.
https://threlte.xyz/
That all seemed fairly obvious to me.
You might be getting downvoted for saying it's an "atypical" way of using three.js. the pmnd.rs community (for example) is quite large.
I understand why people like the declarative nature of react three fiber, but it's quite unfortunate that it requires something like code sandbox to allow modification / working with it on the web- but that's by nature due to being react.
Vanilla three.js can be written surprisingly similarly, if you are disciplined about breaking things up into functions/components. And no react necessary / can embed a code editor and allow direct modification.
For what it’s worth, this is also true of whatever else one might express with JSX: imperative code (and syntax, and semantics) can be structured in a way that closely resembles declarative code… with discipline.
It’s doesn’t have to be especially onerous discipline if you embrace it, but it becomes considerably more onerous as it becomes more social: if some members of a team/contributors to a project embrace it more/less than others, that difference in commitment becomes a constant source of friction.
> It’s doesn’t have to be especially onerous discipline if you embrace it, but it becomes considerably more onerous as it becomes more social: if some members of a team/contributors to a project embrace it more/less than others, that difference in commitment becomes a constant source of friction.
That's one of the stronger arguments for opinionated pre-processors/frameworks/libraries like Typescript/TSX/JSX/React in general. Because it abstract away those things that only some team members would embrace, you effectively make everyone embrace them. That leads to less friction.
But this reduced friction comes at a cost: more complex abstractions and incidental bugs related to that complexity. And as far as the procedural vs declarative: after a certain degree of complexity, I find myself introducing procedural codes within useEffects, useMemos anyways
Has ThreeJS updated to start using X3D (https://www.x3dom.org/) yet?
Taking a quick look at [the docs](https://r3f.docs.pmnd.rs/getting-started/introduction) linked above, I see the use of Canvas. Fair enough.
I start using X3D at work in order to enable some cool functionality showing steps to build an assembly in 3D, and it's actually insane how high the performance is. It's all HTML components, so I wrote a super thin React wrapper (essentially the scene needs to be manually loaded once, that's about all) and have been cruising with that.
Effortless to fly, rotate, hide or highlight parts, decompose things into x3d assets, and so on.
It might have issues somewhere, I haven't widely tested browser compat or anything, but it's curious to me how little I see it versus how much I see ThreeJS when it seems that X3D is the more capable platform.
That's like asking "Has React updated to start using Angular".
Even more than that. x3dom looks like the kind of thing that could use threejs under the hood.
GP - are we misunderstanding something here?
Another example of this is React-Native-Skia: https://shopify.github.io/react-native-skia/docs/getting-sta...
It uses a virtual DOM to diff the desired state and the current one: https://shopify.github.io/react-native-skia/docs/canvas/cont...
If you're interested how it's implemented, that's the source code: https://github.com/Shopify/react-native-skia/blob/main/packa...
been there - love how clean react-three looks at first but man, once you hit real perf walls, its like, why am i even using this lol. you think the push for these declarative libs is just for quicker onboarding or something deeper?
I tried to build a game with react expo and react three last year and it was painful to me. There were version mismatches and some features didn't work if I combined them in some ways that I needed sooner or later when trying to finish a project. I believe it is correlated with the fact I tried this on mobile, but maybe someone else has made the same experience with it. At this point I start to evaluate technologies end to end to not be surprised when projects break at a certain level of maturity or in size when they get close to release in terms of feature completeness.
When I was new to the integration of threejs and react I found these examples to be just amazing.
https://r3f.docs.pmnd.rs/getting-started/examples
The screenshots look cool, but (on my iPhone) the 1st one I tried to view was in a code sandbox that threw an error.
The examples here
https://threejs.org/examples/#webgl_animation_keyframes
seem to work great on my phone...
R3F is great but almost all the code sandbox demos have been broken for years at this point
Yeah, they went all in on Codesandbox early on, and at the time that was a good idea.
But very soon after Codesandbox made the switch to containers and started chasing monetization. I saw an interaction between the creators of each on Twitter at the time where Codesandbox promised not to throttle or limit R3Fs examples. But I don't think they fully kept that promise.
Sounds annoying. I have posted several examples on the R3F Discord of broken sandboxes but no one wants to fix them so I stopped bothering.
All the broken ones I saw were weird things like model/asset files just not being there at all.
Changing priorities I guess. I forget the name of the guy who originally created R3F but he is really skilled at making beautiful examples. I think he's much less involved now - the people I see responding to PMNDRS GitHub issues these days are focused on the technical stuff rather than docs and examples.
I'm guessing that Safari on iOS doesn't support any of the necessary webgl here? Every example shows up with a blank component with a question mark emoji at the center.
Cool but the docs links should take you to the docs, not the github source of the docs. Expected a demo link of each thing.
Is there a Svelte equivalent?
I found Threlte[1] recently and enjoy not having to write low-level Three.js code to make things happen.
---
[1]: https://threlte.xyz
How does this compare to https://aframe.io ?