[HN Gopher] React Renderer for Three.js
       ___________________________________________________________________
        
       React Renderer for Three.js
        
       Author : oleksiitwork
       Score  : 154 points
       Date   : 2021-08-15 16:18 UTC (6 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | [deleted]
        
       | miklosme wrote:
       | When AR/VR finally happens, UI developers will have to deal with
       | complexity from a completely different paradigm. For me, React's
       | biggest strength has always been its ability to organize
       | complexity into a manageable order. Combine this with the large
       | pool of developers and extensive ecosystem, I think React will be
       | the go-to tool for AR/VR apps. For this reason, I'm super hyped
       | for R3F.
        
         | webgfxdev wrote:
         | React is one of the worst choices of doing something like that.
         | 
         | The underlying abstraction model of having a tree of components
         | and re-rendering only the parts that have changed between
         | renders doesn't map to the hardware at all, meaning you'll
         | waste most of the HW performance just on maintaining the
         | abstraction.
         | 
         | You'll also get zero benefits from the third-party libraries -
         | there's nothing in them that can help you with stuff that
         | matters, like minimizing amount of the GPU state transitions
         | for example or minimizing amount of GPU/CPU syncs.
         | 
         | It will be scenegraphs all over again, and the graphics
         | industry has ditched these long ago in favor of simpler models,
         | for good reasons.
         | 
         | Long story short, the happy path in graphics programming is
         | very narrow and fragile, and you typically want to structure
         | your abstraction around it.
        
         | pjmlp wrote:
         | BabylonJS and PlayCanvas do it just right without jumping into
         | React fashion.
        
         | [deleted]
        
         | rock_hard wrote:
         | Totally agree!
         | 
         | We switched https://flux.ai from vanilla ThreeJS to R3F and
         | it's been a huge productivity gain for the whole team!
         | 
         | Less code, that is more capable and more reusable!
         | 
         | In case anyone is interested, we are hiring:
         | https://coda.io/@flux-ai/flux-jobs
        
           | nobbis wrote:
           | Is this AR/VR? It looks like 2D circuit schematics.
        
             | rock_hard wrote:
             | It's 2D schematics and 2D/3D PCB layout. All powered by r3f
             | though
             | 
             | PCB layouting hasn't publicly shipped yet though
        
         | hirako2000 wrote:
         | Except that for three.js, it's react introducing complexity
         | rather than improving the organisation of the code. A simple
         | component with defaults look neat, but start building a complex
         | scene and jsx gets in the way.
         | 
         | three.js isn't dom elements updated in js. The state of each
         | object is updated in the scene depending on more than whether
         | they changed.
         | 
         | Where three.js lacks abstraction is a component system, in
         | plain js, to organise application with decent patterns. Most
         | three apps are a big blob or code.
        
           | runawaybottle wrote:
           | Okay yes, thank you from saving me from my drivel. Why would
           | three.js care about representing a document model, bubbling
           | up events, and so on. If we do this, we do it fresh.
        
         | [deleted]
        
         | nobbis wrote:
         | As a counterpoint, I've enjoyed building VR on the web using
         | C++ (with WebAssembly/WebGL/WebXR) and not having to touch the
         | DOM or JS, see:
         | https://twitter.com/nobbis/status/1425266634982248451
         | 
         | Benefits include complete control at frame and pixel level,
         | being cross-platform (same code runs on web, iOS, macOS,
         | Linux), and having access to third-party C/C++ libraries for 3D
         | graphics.
        
           | nightski wrote:
           | I have not put a lot of time into learning WebAssembly. But
           | isn't WebGL a JavaScript API? Meaning wouldn't you be going
           | from WebAssembly -> JavaScript engine -> WebGL? I was under
           | the impression WebAssembly had no access to the outside world
           | and could only access the relevant JavaScript APIs. But if it
           | is true you can basically do WebAssembly -> native GL then
           | that would be amazing.
        
             | Jasper_ wrote:
             | emscripten ships a "desktop GL" emulation library [0],
             | which can have quite a bit of overhead. If you want
             | something faster, you can use the native WebGL bindings [1]
             | 
             | [0] https://github.com/emscripten-
             | core/emscripten/blob/main/src/... [1]
             | https://github.com/emscripten-
             | core/emscripten/blob/main/test...
        
               | nobbis wrote:
               | The "native WebGL bindings" still call into JS.
               | 
               | Desktop GL emulation is just a layer on top OpenGL ES if,
               | for example, you're still using OpenGL's fixed function
               | pipeline (deprecated 13 years ago.)
        
             | nobbis wrote:
             | You're correct. WebGL does require extra validation
             | compared to native GL, but it's effectively the same API as
             | OpenGL ES 2.0/3.0 and Emscripten handles the translation
             | from C/C++ for you.
             | 
             | There's some overhead but it's negligible (assuming you're
             | not making overly redundant API calls.)
        
               | rock_hard wrote:
               | This is how Figma does it
        
           | brailsafe wrote:
           | This looks really neat
        
         | short_sells_poo wrote:
         | I think they need to take a very serious and hard look at
         | performance before it can go anywhere near VR (where rendering
         | speed and stability are paramount). I'm sure it works for
         | simple things and can handle GUIs fine, but the overhead seems
         | huge currently.
        
           | mlsarecmg wrote:
           | there are many large scale apps built with it these days. it
           | was initially made for complex use cases, to bring order into
           | the scene graph, and of course to optimize raw rendering
           | performance: https://docs.pmnd.rs/react-three-
           | fiber/advanced/scaling-perf...
        
       | beebeepka wrote:
       | One of those things that make zero sense but use react, and that
       | makes them cool enough to blog about.
        
       | throw_m239339 wrote:
       | VRML anyone?
        
         | mlsarecmg wrote:
         | you can't express a 3d scene with markup alone, that is why
         | VRML didn't succeed. the JSX you see just masks function calls,
         | it is not XML or HTML, but the true power is in components and
         | hooks (useFrame, etc). a r3f component is self-contained, it
         | will even subscribe to the render-loop. click into these two
         | examples to see the difference:
         | https://twitter.com/0xca0a/status/1426924274527477764
        
           | e12e wrote:
           | > you can't express a 3d scene with markup alone
           | 
           | What?
        
             | mlsarecmg wrote:
             | you want your view to do something, to participate in the
             | render loop, to animate, user interaction etc. look at the
             | example on the main page: https://github.com/pmndrs/react-
             | three-fiber#what-does-it-loo...
        
               | dheera wrote:
               | So just do it with VRML5 like HTML5.
               | 
               | <cube id="myCube" x="0" y="0" z="0" width="10px"
               | height="10px" length="10px" onClick="someFoo()"
               | onMouseOver="someOtherFoo()" onTouchStart="someBar()">
               | 
               | <video src="blah.mp4" rotateX="45deg" rotateY="30deg"
               | autoplay="true" loop="true"
               | onClick="document.querySelector('#myCube').rotateX(45);">
               | 
               | ...
               | 
               | No reason the above can't be done. But many people will
               | come up with lame excuses about why we shouldn't have
               | nice things.
        
         | moron4hire wrote:
         | There's always one of you. It's not funny.
        
       | tills13 wrote:
       | Is there an error in the examples? You have
       | const mesh = useRef()              ...              <mesh
       | ref={mesh} ...
       | 
       | You'll be rendering an undefined element (before the ref has a
       | chance to attach).
       | 
       | Also, the TypeScript example makes me head hurt.
       | const ref = useRef(null!)
       | 
       | Ah, yes. A non-null null literal.
        
         | shallowthought wrote:
         | I have to wonder: Have we gone too far? Is TypeScript becoming
         | more tedious than helpful? Has React lost its way in its blind
         | zeal for pure functions?
         | 
         | ...no, no, definitely not. Must've gone crazy for a second
         | there.
        
           | tills13 wrote:
           | I think I blame JavaScript for this mess, not the other way
           | around.
        
         | CraftThatBlock wrote:
         | I'm guessing they are overloading the ref's usage with the
         | custom renderer. I believe that a renderer can accept non-
         | function/class components in theory, which is why this works.
         | 
         | I don't really like this though, although interesting, it feels
         | like a hack and might not integrate well with tooling (e.g. the
         | Typescript example).
         | 
         | How I would have implemented it was exposition a Mesh component
         | which wraps this, and a useMesh hook to wrap and type the ref.
        
           | mlsarecmg wrote:
           | r3f is a custom renderer, there is no difference in useRef
           | between a div or a mesh, refs give you the underlying object.
           | lowercase elements are native elements (div, span, mesh,
           | view, box), they are defined by the renderer. uppercase is
           | for components.
        
             | tills13 wrote:
             | Ah, if they're using a custom JSX Pragma it might make
             | sense. Still a little too bespoke for me.
        
               | mlsarecmg wrote:
               | no special jsx pragma needed. this is actually just plain
               | react, <div> <span> and so on come from react-dom, which
               | defines these elements. other renderers define theirs.
               | here's a mini custom renderer if this interests you:
               | https://codesandbox.io/s/reurope-reconciler-hd16y
        
           | yashap wrote:
           | Yeah, I don't have experience with this lib, but the example
           | code looks overly magic. But I'm also more of a backend dev,
           | and find the useRef API to be inherently confusing, so that's
           | probably part of it.
        
         | afloatboat wrote:
         | I believe the `null!` is some trickery to let the rest of the
         | code know that the `ref.current` will never be null, but with
         | how React works you can't really provide any other value. It
         | prevents having to check for `null` everytime you address the
         | value.
         | 
         | I don't understand your first issue. The component is assigned
         | to the ref, not the other way around, so why would it render
         | `undefined`? It's possible that `mesh.current` is undefined at
         | first render, but that shouldn't matter for the component, only
         | for the reference.
        
           | tills13 wrote:
           | Oops, I guess it's not undefined, but it'll attempt to render
           | a ref object.
           | 
           | const mesh = useRef()
           | 
           | <mesh />
           | 
           | The underlying JavaScript (i.e. not JSX) on the first render
           | , is React.createElement(mesh /* { current: undefined } */, {
           | ref: mesh } )
           | 
           | In fact, I don't even think it matters which render you're
           | talking about. It's still rendering a ref (again, { current:
           | <undefined or a Three.MESH, I guess> }). That doesn't seem
           | right to me.
        
             | afloatboat wrote:
             | Ah, right. But the mesh useRef is not wat is being
             | rendered, the `mesh` component is more like rendering a
             | div. If you open the sandbox and remove the useRef mesh and
             | references it will still work.
             | 
             | Pretty confusing though, would have been better to call it
             | `meshRef`. I also expected these components to be
             | capitalised, but it probably makes sense in the way that in
             | the dom renderer default components are lowercase as well.
        
         | sswu wrote:
         | The TypeScript example does feel a little weird. I've used this
         | library successfully with React + TypeScript with the following
         | pattern:                 const mesh = useRef<THREE.Mesh>(null);
         | // mesh has type RefObject<THREE.Mesh | null>       ...
         | useFrame((): void => {         if (mesh.current != null) {
         | mesh.current.rotation.x += 0.01;         }       });       ...
         | return <mesh ref={mesh} ... />
         | 
         | As others have mentioned, the ref will typically attach by the
         | time a `useEffect` hook is called, but for safety it's nice to
         | have the null check.
        
         | onion2k wrote:
         | The useRef hook doesn't return a ref. It returns a function for
         | setting the ref value. The value is in ref.current. The ref
         | prop calls the setter with a ref to the element on mount.
        
         | mlsarecmg wrote:
         | this is how refs work. it's the same with a div. the ref will
         | be filled in useEffect, not before.
         | 
         | null! is a common typescript thing, it is a semantic guarantee
         | that the ref is static and hence will be available. this saves
         | you the if (ref.current) { ... } check.
        
           | tills13 wrote:
           | The ref isn't used in an effect, though, at least not in the
           | provided code.
        
           | tills13 wrote:
           | To clarify, I'm not talking about the ref _prop_, I'm talking
           | about them using the ref as the element.
        
             | mlajtos wrote:
             | It's lowercase, so it will act as plain-old tag. If the
             | name would be uppercase, that would be a bug you described.
        
             | mlsarecmg wrote:
             | no ref is used as an element in that code.
             | const ref = useRef()         useEffect(() =>
             | console.log(ref), [])         return <div ref={ref} />
             | 
             | will return { current: [dom node] }                   const
             | ref = useRef()         useEffect(() => console.log(ref),
             | [])         return <mesh ref={ref} />
             | 
             | will return { current: [mesh node] }
        
       | [deleted]
        
       | gentleman11 wrote:
       | So react builds a tree of react elements that react dom turns
       | into html. So this must replace react dom?
       | 
       | Reusable components are easy in a 3d engine like 3js. You can
       | still program declaratively if you liked. It's claim to
       | outperform raw threejs is surely untrue. React is also bad for
       | animations, and they recommend you sort of use reacts "back door"
       | to do complex animations. 3d engines are all about animations.
       | You could use redux easily without react if you liked. I bet you
       | still have to learn a new API.
       | 
       | This doesn't seem that useful. Am I mistaken? (Honestly curious)
       | 
       | That said, I bet it was interesting and pleasantly challenging to
       | write
        
         | runawaybottle wrote:
         | The interesting thing about switching renderers is that you are
         | now free of the DOM. I just think web developers only know one
         | api really well (the DOM and it's offspring frameworks) that
         | when you give us a blank canvas (no pun intended), we resort to
         | the same data structure and api of what we've always known.
         | 
         | We're truly free to make a <Modal /> however we like. Perhaps
         | not even in those tags. We just don't know it yet.
        
           | eyelidlessness wrote:
           | JSX has very little to do with the DOM. It's always been a
           | language extension to provide render-agnostic expressions.
           | Non-DOM renderers have existed for years (React Native, smart
           | TV, CLI, PDF, PowerPoint, the list keeps going...).
           | 
           | The reality is it's a good abstraction because it's so
           | decoupled from the DOM, but it's associated with the DOM and
           | web mainly for historical reasons.
           | 
           | It's also worth noting that there are similar extensions to
           | other languages that are similarly render-agnostic.
        
             | runawaybottle wrote:
             | So I have a to contest that a bit. It parallels the DOM in
             | structure entirely. You can see how this is the truth when
             | you want to have components outside of a parent-child
             | hierarchy belong to the same 'component', you have to use
             | this very abstract concept of a Portal, as in, nothing is
             | natural once you start thinking out of the tree structure.
             | 
             | Certainly you can map this api to a variety of other APis,
             | which I'm sure is what they did with React Native, but it
             | was built with the DOM tree structure in mind.
             | 
             | I think it's one of the most brilliant things frontend has
             | ever created along with Jquery, but they are both slaves to
             | the DOM.
        
               | eyelidlessness wrote:
               | > So I have a to contest that a bit. It parallels the DOM
               | in structure entirely. You can see how this is the truth
               | when you want to have components outside of a parent-
               | child hierarchy belong to the same 'component', you have
               | to use this very abstract concept of a Portal, as in,
               | nothing is natural once you start thinking out of the
               | tree structure.
               | 
               | This isn't a property of JSX, it's a property of a
               | virtual DOM. The former is typically used with the
               | latter, but don't have to be. SolidJS is an example of
               | JSX without VDOM. It compiles to plain DOM operations,
               | you only have a component tree during development. And
               | like React, its compiler was designed to be render-
               | agnostic and can be/is used in other environments.
               | 
               | It's funny you should mention Portal here. I am working
               | on a technique/hopefully eventual library to
               | transparently (without requiring developer intervention)
               | use Portals as a partial hydration solution--completely
               | sidestepping the tree structure and only rendering
               | interactive components. I believe it will be framework
               | agnostic for anything that provides (or can provide) a
               | hyperscript/createElement function and Portal-like
               | functionality. And like those frameworks, it's just a
               | declarative data structure and can render to anything.
               | 
               | > Certainly you can map this api to a variety of other
               | APis, which I'm sure is what they did with React Native,
               | but it was built with the DOM tree structure in mind.
               | 
               | I've spent a lot of time (maybe too much time) looking at
               | the underlying renderer abstractions of React, Preact,
               | Solid, JSX Lite, several others.
               | 
               | The only one tightly coupled to DOM is Preact (and even
               | that isn't totally coupled). In fact that coupling is one
               | of the major advantages Preact has in terms of package
               | size.
               | 
               | React's design has a separate renderer
               | interface/abstraction that is primarily oriented around
               | state reconciliation; the DOM implementation is just one
               | of many.
               | 
               | Solid's JSX (implemented in a Babel transform somewhat
               | misleadingly called dom-expressions) is similarly
               | decoupled from its DOM implementation with an abstract
               | renderer interface. The difference is rather than state
               | reconciliation it's reactive.
               | 
               | JSX Lite's render target is even more abstract, it
               | renders an intermediate data structure that can be
               | transformed to other component libraries, and even some
               | design apps.
               | 
               | You can implement a JSX transform to basically any render
               | target, without React. You can even skip the entire
               | notion of state, events, interaction.
               | 
               | I even use it on my personal site to generate PNGs at
               | build time! Unless you're looking at my source code you'd
               | never know it.
        
               | eyelidlessness wrote:
               | Correction: Solid's dom-expressions compiler is more
               | directly coupled to DOM APIs than I remembered. In
               | hindsight this makes sense given the design, but I
               | imagine it could provide the level of abstraction I
               | misremembered. It would probably harm performance without
               | some second-pass build time inlining.
        
               | brundolf wrote:
               | Lots of things are already tree-structured though,
               | including Three.js
        
         | Jasper_ wrote:
         | The creator of React-Three-Fiber has come out with some
         | extremely bizarre tweets, like claiming updating 2,000 cubes at
         | 60fps is an "impossible amount of load" [0].
         | 
         | The solution it uses for performance is... punting your
         | calculations to the next frame if you didn't make it this
         | frame. They call this "the scheduler". Turns out this makes no
         | sense in any serious context if you want your 3D frame to be
         | coherent.
         | 
         | I'm sure this is helpful for some people, but I can't rely on
         | it when making serious applications. There's lots of things you
         | can punt to the next frame, but ideally that should be decided
         | per system, and not a global framework.
         | 
         | Keep in mind that _rendering_ here is still happening every
         | frame, so the only thing that 's happening is 2,000 transform
         | updates for cubes. Inexplicably, without the scheduler that
         | punts updates to the next frame, it takes 700ms to do this [1].
         | For 2,000 matrix muls. That's 3ms per cube. What on earth is
         | this doing?
         | 
         | [0] https://twitter.com/0xca0a/status/1199997552466288641 [1]
         | https://twitter.com/0xca0a/status/1199997561358213120
        
           | dheera wrote:
           | > updating 2,000 cubes at 60fps
           | 
           | Have we gotten so bloated with framework on top of framework
           | that even this is deemed an impossible load?
           | 
           | If you write straight-up WebGL code 2000 cubes at 60fps
           | should be a walk in the park for any modern PC.
        
           | mlsarecmg wrote:
           | as absurd as it may seem to you, this is a react feature. the
           | upcoming react 18 release is pretty much based on
           | concurrency.
           | 
           | this is the test you are referring to:
           | https://docs.pmnd.rs/react-three-fiber/advanced/scaling-
           | perf...
           | 
           | the github repo now contains a vanilla test that you can run
        
             | Jasper_ wrote:
             | As I said, "schedule" just means "defer work to future
             | frames". That's a valid strategy if you have severe load,
             | but it's not globally applicable: one might imagine that
             | two components _need_ to be updated together (e.g. updating
             | the arms and body of a character should not be split up
             | between two frames, or else the user will see split bodies
             | for in-between frames). At the very best, this sort of
             | scheduling should be informed by gameplay systems.
             | 
             | It can "effortlessly outperform" only if we're talking
             | about throughput, not latency here -- the scheduler ensures
             | that less is done each frame so we can meet 60fps each
             | frame, at the expense of having things done frames later
             | than when they probably should have been.
             | 
             | I'm aware this is all a React feature. I disagree with the
             | React team that "concurrency" is a usable solution to
             | performance in all cases. But I can respectfully disagree
             | with them about that. I can understand why a scheduler
             | helps improve user-perceived performance. I'm happy to talk
             | about what I believe are the tradeoffs.
             | 
             | Regardless of _all of that_ , updating 2,000 cubes should
             | _never_ cause 700ms of load to begin with. The test you
             | linked me to is not the same test. The test in the tweet is
             | seen here [0], and has no artificial runtime delay as far
             | as I can tell. For extra irony, note that they _already_
             | have to bypass React (which is described as ItemSlow), in
             | favor of the  "Zustand approach", aka modifying things
             | imperatively.
             | 
             | Remember, Three.JS is _already_ running every frame, and
             | rendering every frame, with or without a scheduler. That
             | means that the 700ms overhead has to be coming from the
             | React  / R3F part of the demo.
             | 
             | [0] https://github.com/pmndrs/react-three-
             | fiber/blob/e3a71baad42...
        
               | mlsarecmg wrote:
               | what you link there has more to do with react vs zustand.
               | 
               | if it interests you, read up on react 18 concurrency,
               | this is the bit you are missing in this discussion.
        
               | Jasper_ wrote:
               | No; that's not the test in question. The tweet I started
               | the conversation with [0] shows a bunch of cubes, not
               | text geometry. It's not the text geometry test. It's just
               | creating and manipulating a bunch of cubes.
               | Unfortunately, it was removed from the react-three-fiber
               | examples, so it can't be easily run unless you check out
               | an old version of the repo. It contains two switches, one
               | for React vs. Zustand, and the other for Concurrency On
               | vs. Off [1]. The tweet is talking about the concurrency
               | mode.
               | 
               | I'm very familiar with React 18 concurrency, and gave my
               | detailed analysis of it. The documentation you linked
               | even confirms my analysis of it deferring work across
               | frames:
               | 
               | > it can potentially defer load and heavy tasks
               | 
               | I've already given my feedback about that approach. I
               | heavily suspect the overhead here is all React's
               | reconciler / differ, as Svelte, which has no scheduler,
               | performs similarly to the concurrent React mode[2].
               | 
               | [0] https://twitter.com/0xca0a/status/1199997552466288641
               | [1] See the description in the panel here. It's talking
               | about React 18 concurrency.
               | https://github.com/pmndrs/react-three-
               | fiber/blob/e3a71baad42... [2] https://twitter.com/Rich_Ha
               | rris/status/1200805237948325888
        
               | mlsarecmg wrote:
               | i've written them. the first pits react against zustand,
               | it naively lets react churn through the whole graph 60
               | times per sec, you wouldn't do that ever, but zustand
               | could. i initially tweeted it for people interested in
               | that lib.
               | 
               | some got it in the wrong throat, so i changed the test to
               | _actually_ pit it against a vanilla counterpart, and they
               | were silent. the real test, contest that please, let the
               | zustand thing go.
        
               | Jasper_ wrote:
               | Ah, so you're @0xca0a. Sorry, I didn't recognize you at
               | first. The examples and documentation I'm quoting at you
               | are your own; my mistake.
               | 
               | That said, I don't know why you entered into the
               | conversation by asserting properties about a different
               | test than the one that started the conversation. I
               | recognize that TextGeometry is slow and CPU-heavy, and
               | that the deferred work approach has some value in helping
               | to manage that, but the cube test we started the
               | discussion with had no artificial delay as far as I can
               | tell, nor do I see it really doing any CPU-heavy work.
               | For cases without any obvious CPU-heavy work, where does
               | the 700ms overhead come from?
               | 
               | > and it stupidly and naively lets react churn through
               | the whole graph 60fps, something you would under no
               | circumstance do
               | 
               | Iterating over 2,000 objects is something we do all the
               | time in game engines (where I'm from and where I work).
               | I've written particle systems that handle far more than
               | that; I just checked the particle system for a game I'm
               | working on, and the simulation time maxes out at <1ms,
               | and I haven't even SIMD'd it, though it is somewhat
               | SoA'd. React's inability to handle a low workload like
               | 2,000 items is why I believe React is a poor fit for
               | serious 3D applications.
               | 
               | And yes, I would argue against React 18 Concurrency's
               | approach as a general design. Deferring work between
               | frames is an incredibly valuable tool, but I don't
               | believe it's something that validly can be done globally,
               | or that it's a solid basis for a 3D framework. It trades
               | low latency for high throughput, and I believe that's a
               | tradeoff that should be made _by the application author_.
               | 
               | Multiplayer games and VR often require very careful
               | controlled latency, and having unpredictable latency can
               | make your game unplayable, or make someone incredibly
               | motion sick.
               | 
               | We're clearly going in circles on this point; I'll shut
               | up at this point because clearly we're at an impasse, and
               | you're (completely validly) unwilling to discuss the
               | cubes example that started things, which I would prefer
               | to cross-examine in more detail. My approach to
               | performance analysis is to profile things, understand
               | bottlenecks, and come up with targeted fixes before going
               | with more global approaches with large tradeoffs, so I'd
               | be personally be more interested in knowing where the
               | time is spent.
               | 
               | Finally, and this is my experience speaking as a game
               | engine engineer, I think you should think more about how
               | much work you expect to be able to do in one frame, and
               | set your sights there. You can easily update 2,000
               | objects with minimal overhead.
        
               | mlsarecmg wrote:
               | i am sorry but there is a deep misconception here. of
               | course you can update thousands of things, why wouldn't
               | you.
               | 
               | try racing game for instance:
               | https://twitter.com/0xca0a/status/1400164834243719173
               | 
               | or space game:
               | https://twitter.com/0xca0a/status/1184586883520761856
               | 
               | you won't find react in the browsers perf readout except
               | for short blips when the tree structure is changed. you
               | do not update fast things with setState. since games are
               | render-loop driven, you mutate, and that is also how r3f
               | works.
               | 
               | as for react 18, yes, it works global. the entire
               | component tree is virtual and can be prioritized. you
               | could say, _this_ thing back there is less important than
               | physics driving my rocket, defer please. this will be an
               | incredible tool for games going forward.
        
           | felipellrocha wrote:
           | The "no additional overhead" struck me as a really high
           | claim, and this confirms it. 2000 cubes at 60fps is really
           | nothing if you know what you're doing.
        
             | mlsarecmg wrote:
             | r3f does not introduce overhead. the readme text explains
             | it all https://docs.pmnd.rs/react-three-
             | fiber/advanced/scaling-perf...
        
           | vvanders wrote:
           | The "No. There is no additional overhead. Components
           | participate in a unified renderloop outside of React. It
           | outperforms Threejs in scale due to Reacts scheduling
           | abilities." made me do a serious double-take.
           | 
           | If you want to go fast in this space then you need to care
           | about data layout and how the system is structured end-to-
           | end. Calling a function per object is going to hit a wall
           | regardless of how you schedule.
           | 
           | Hundreds/Thousands of updates is not small but it's also not
           | massively impressive either. I've done ~2,800 node scene
           | graphs on underpowered ARM chips back in '09 at 60FPS
           | including rendering. You have to use NEON, and be aware of
           | your caches. No scheduling magic is going to change that
           | unless you're just deferring work which sounds like what may
           | be happening there.
           | 
           | FWIW I've also done this in Java via FlatBuffers(which uses
           | ByteBuffer internally) to keep data coherency when driving
           | animations frames so it doesn't require dropping down to
           | C/C++/Rust(although C#'s value types do make it easier).
        
             | brundolf wrote:
             | It's interesting because in normal React usage, the
             | overhead of generating and throwing away a bunch of objects
             | on each update is dwarfed by the cost of updates to the DOM
             | 
             | But in Three.JS the actual updates to the tree should be
             | cheap, right? There's no reflow, you're just setting values
             | in memory to be used by the next render frame. If so, that
             | changes the calculus.
             | 
             | There's also the fact that in an app, it's rare for actual
             | state updates (and therefore React renders) to happen on
             | every frame; usually it's only on interactions. Maybe the
             | occasional animation (if it can't be handled by native CSS
             | animations). Whereas in graphical contexts like this, it's
             | much more likely you'll have lots of objects in continuous
             | motion (and therefore continuous re-renders).
             | 
             | I can see the productivity gains being worth it for a lot
             | of simpler use-cases, but I'm skeptical about the
             | performance claims when you start to get into complex
             | scenes with lots of entities.
        
               | vvanders wrote:
               | Yeah, I can totally see the argument that it's a
               | programming model that's well understood and you can get
               | a productivity boost from that. However I don't think you
               | can say that doesn't come with a cost, otherwise it would
               | have been pretty widely adopted across the industry.
        
               | brundolf wrote:
               | > otherwise it would have been pretty widely adopted
               | across the industry
               | 
               | I'm not sure that's a fair explanation for why. It's
               | totally possible to come up with new paradigms that are
               | useful even though nobody's thought of them before.
               | 
               | I would think the main issue will be around JavaScript's
               | tendency (cultural, syntactic, etc) to casually create
               | and release objects all over the place, constantly.
               | There's nothing intrinsically wrong with this, but it
               | seems problematic for this use-case.
               | 
               | Example: JavaScript doesn't have named function
               | parameters, because instead you just create and
               | destructure an object:                 function foo({
               | param1, param2, param3 }) {       }            foo({
               | param1: 'a', param2: 'b', param3: 'c' })
               | 
               | The syntax encourages this, the React docs encourage
               | this. JSX itself does this for every element you render.
               | And for normal JavaScript usecases it works just fine.
               | But when you're running this logic every frame, I would
               | guess it will limit you at a certain point.
               | 
               | Despite that, I think people are onto something with the
               | broader idea of coding a 3D scene declaratively. I'm just
               | skeptical that React or its norms are the right path to
               | doing it at scale.
        
               | vvanders wrote:
               | Hierarchical component based designs have been around in
               | game-dev for ages. I first used them in '05 but I
               | remember prior art even before then. It was pretty common
               | to have a declarative way to define components(usually
               | through a scripting language like Lua or sometimes custom
               | DSL).
               | 
               | I agree on the performance aspect, any inner-loop stuff
               | always was down in a native language or heavily JIT'd
               | path, but even then data layout drove it even more which
               | usually required structuring the upstream systems ahead
               | of the core logic. It's the reason why there's no "one-
               | size fits all" game engine. They all make very discrete
               | trade-offs in terms of entity counts, open world vs
               | constrained layout and the like.
        
             | short_sells_poo wrote:
             | I can't take a 3d rendering framework seriously if it gets
             | bogged down at updating 2000 untextured and non-interacting
             | cubes @ 60 FPS.
             | 
             | What on earth is all that time spent on? Doing ~2000 3x3
             | matrix ops should take a modern processor a few hundred
             | usec surely?
             | 
             | To put the silliness of the 2000 number in context, look
             | here at a showcase of the Unity ECS system from years (!)
             | ago: https://software.intel.com/content/www/us/en/develop/a
             | rticle...
             | 
             | Their starting setup achieves 16000 textured and complex
             | (relatively) models @ 30fps. Which is already much more
             | than the "optimized" thing is achieving here. And it
             | doesn't "cheat" by pretending to be fast via simply not
             | doing to updates that are expected. And once they apply the
             | various optimizations with memory layout etc... they get to
             | 150000 (!) textured models moving about on screen @ 30 fps.
             | So let's say 75000 @ 60fps, which is more than 35x as many
             | objects, and the objects are much more complex.
             | 
             | Am I missing something? Why is "2000 cubes @ 60fps"
             | extraordinary?
        
               | Jasper_ wrote:
               | You have to remember that this 700ms overhead is _not_
               | the renderer. It 's just setting fields on the underlying
               | Three.JS Cube objects. The Three.JS renderer runs fine,
               | and I don't even consider Three.JS to be a fast renderer.
               | So yes, the overhead of React-Three-Fiber's tree
               | reconciliation is seemingly massive. No, I do not have
               | any answers for what it is doing. Nor do I have any
               | guesses.
        
               | mlsarecmg wrote:
               | there were multiple versions of that test, the fist had
               | nothing to do with the subject matter, the ones that
               | people refer to (spinning cubes) had an artificial delay
               | that was added to simulate cpu stress.
        
               | Jasper_ wrote:
               | No artificial delay was added from what I can find.
               | Here's the example from the tweet [0]. Feel free to tell
               | me where the artificial delay is.
               | 
               | [0] https://github.com/pmndrs/react-three-
               | fiber/blob/e3a71baad42...
        
               | mlsarecmg wrote:
               | we're running in circles unfortunately and i've explained
               | where the test you're referring to comes from and what it
               | meant. i've posted the real test and if you want, engage
               | in it.                   async function test() {
               | const chars = `!"SS$%&/()=?*#<>-_.:,;+0123456789ABCDEFGHI
               | JKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`
               | const font = await new Promise((res) => new THREE.FontLoa
               | der().load("https://raw.githubusercontent.com/drcmda/sche
               | duler-test/master/public/Inter%20UI_Bold.json", res))
               | console.time("test")           for (let i = 0; i < 510;
               | i++) {             new
               | THREE.TextGeometry(chars[Math.floor(Math.random() *
               | chars.length)], {               font,               size:
               | 1,               height: 0.5,
               | curveSegments: 80,               bevelEnabled: false,
               | })           }           console.timeEnd("test")
               | }              test()              // To really drive it
               | home you'd have to repeat it every two seconds ...
               | // setInterval(test, 2000)
               | 
               | how react 18 concurrency works exactly, i think that's
               | not the right place to churn through it. the react team
               | has published tons of reading material as well as public
               | talks.
        
               | short_sells_poo wrote:
               | Thanks for clarifying. 700ms overhead is massive. It
               | sounds like the approach is just deeply suboptimal for
               | any sort of high performance and complicated 3d scenes.
               | Perhaps that was not their objective...
        
         | mlsarecmg wrote:
         | react has little to do with html. it just calls functions, what
         | you see in react-dom isn't html either, these are nested
         | document.createElement(...) calls. this can be configured for
         | any platform, web, native or otherwise, so <mesh/> in this case
         | is just new THREE.Mesh(). in react terms it's called a custom
         | renderer.
         | 
         | the other thing you say, that react is bad for animations --
         | r3f operates outside of react, there is no overhead, it
         | actually quite easily outperforms threejs. but there are many
         | other benefits, it will usually save you lots of code, and it
         | can be more memory efficient, see this thread:
         | https://twitter.com/0xca0a/status/1426924274527477764
         | 
         | the biggest advantage is the component model, because it allows
         | for a true eco system, something that threejs does not
         | otherwise have due to the lack of a common ground. and interop.
         | every react library can now act on meshes and materials.
        
           | runawaybottle wrote:
           | So again, I have to contest this. Not all UIs should be
           | described in a tree structure. React and it's whole diffing
           | algo is to make sense of parts of the tree that changed.
           | 
           | That idea didn't come out of the blue, it came from html, it
           | came from the DOM.
           | 
           | If I give you a blank sheet of paper and say 'write me a
           | rendering api', and you immediately reach for a tree
           | structure, I'd be compelled to say you are influenced by
           | html.
           | 
           | I feel like the watershed moment for us will be that moment
           | in the Matrix where the kid tells Neo 'there is no tree,
           | there is no spoon'.
           | 
           | Edit (full quote from The Matrix):
           | 
           |  _Do not try and bend the spoon, that 's impossible. Instead,
           | only try to realize the truth... There is no spoon... Then
           | you'll see that it is not the spoon that bends, it is only
           | yourself._
           | 
           | We have been bending over for the DOM for quite some time
           | now, I think it's time we explore.
           | 
           | ----------------
           | 
           | Edit 2 (being rate limited), reply to some posts below:
           | 
           | Interestingly enough, ever major performance trick on the
           | frontend requires breaking out of the tree structure. If you
           | want a data table with a notable amount of rows, you have to
           | break out and start defining heights and positions and
           | calculate what to show manually. In other words, the free
           | stuff we were supposed to get from the tree were not free.
           | 
           | My irreverence for the DOM comes from the fact that we have
           | to negate it, ignore it, to achieve certain things.
           | 
           | So in the end I wonder, why bother with it at all?
        
             | mlsarecmg wrote:
             | hard to read through all that tbh. i mostly do not
             | understand what you are talking about when threejs is
             | clearly a tree that react expresses with complete ease.
             | groups in groups with meshes, which have materials, etc. it
             | seems to me you have not worked with threejs before. r3f
             | just expresses threejs, in the same exact same shape you'd
             | have in an imperative app.
             | 
             | there are dozens, hundreds of demos now that are testament
             | to this approach: https://docs.pmnd.rs/react-three-
             | fiber/getting-started/examp... this is slowly becoming the
             | norm of how you write a 3d app or game.
        
             | shadowgovt wrote:
             | Not all UIs should be described in a tree structure, but a
             | tree is an isomorphism to a scene graph, and a scene graph
             | is a very solid, traditional way to structure a 3D scene
             | for rendering.
        
             | atom_arranger wrote:
             | I'm not sure it's optimal from a performance perspective,
             | but having everything represented by a tree is kind of
             | nice.
             | 
             | I haven't really encountered anything that can't be
             | represented well in a tree, especially with context
             | providers in the mix.
        
             | catlifeonmars wrote:
             | While I strongly suspect you're right about React being
             | influenced by the problem space, I'd contest the notion
             | that trees aren't useful for computational constructs in
             | general. Trees are fundamental to the concept of a function
             | call graph over time, for example.
        
               | Jasper_ wrote:
               | In most production renderers, you have a soup of objects.
               | Sometimes it's a tree, but much more often it's a graph
               | of nodes pointing to each other.
               | 
               | One of the first steps of this is to gather all the nodes
               | in the graph, flattening it into a list, and then start
               | filtering and sorting it: for every opaque object, you
               | likely toss it in Z-pre and opaque buckets (for objects
               | visible through the main frustum), and a shadow caster
               | bucket (per shadowed light frustum!). So the renderer
               | itself really prefers lists of lists, it does not have
               | trees internally.
               | 
               | React-Three-Fiber seems to take the DOM-equivalent tree,
               | sort it, and then construct a Three.JS scene graph from
               | it, and then Three.JS's render method starts from that
               | scene graph soup and does the above. So one might argue
               | that the tree is a DOM-like interface you're adding on
               | top of Three.JS's scene graph.
        
               | mlsarecmg wrote:
               | it does not take a dom-equivalent tree, it just expresses
               | the same code you would write out imperatively in a
               | declarative way. threejs is a nested graph after all.
        
               | catlifeonmars wrote:
               | What is a "DOM-equivalent" tree? Do you just mean a tree?
        
         | joshwcomeau wrote:
         | I've built "vanilla" Three.js projects, and worked with react-
         | three-fiber. It is _incredibly_ useful.
         | 
         | Having a new set of "primitives" that map to three.js objects
         | and being able to render them in a React application makes so
         | many things easier. It's also just way less code to write
         | compared to standard three.js.
         | 
         | RE: animation performance, I haven't had any issues with it. I
         | presume either they're doing a bunch of optimizations, or the
         | concerns are greatly exaggerated. You can check out an app I
         | built using it if you want some proof: https://beatmapper.app/
        
           | felipellrocha wrote:
           | It's just what you're rendering is getting nowhere near
           | pushing the limits of what can be done with the gpu.
        
             | miohtama wrote:
             | For some use cases, like charts and diagrams, even a piss
             | poor performance is enough as long as it is accessible to
             | developers.
        
             | mlsarecmg wrote:
             | if you have a threejs app that pushes the limits, the react
             | counterpart will merely do the same, only with less code.
             | threejs renders exclusively.
        
             | rock_hard wrote:
             | It's not mutually exclusive though
             | 
             | R3F gets you up to speed really quick and helps to easily
             | onboard a larger engineering team and stay productive
             | 
             | At some point your scenes will become complex enough that
             | you will want to start using instanced meshes or just move
             | all critical things into shaders all together...and you
             | totally can do that.
             | 
             | In our case r3f made these performance optimization even
             | easier because it's so modular
        
       | franciscop wrote:
       | We're using these at my job, and it's been a pretty amazing
       | experience. We built a wrapper[1] so that every front-end dev can
       | build components even easier (without needing to know Three.js),
       | since in the end of the day they are just React components that
       | can be easily composed together:                   <View3D>
       | <Box position={[1, 0, 0]} color="green" />           <Box
       | position={[-1, 0, 0]} color="red" />         </View3D>
       | 
       | [1] https://www.npmjs.com/package/@standard/view
        
       | nojvek wrote:
       | How are they making custom Tags with lowercase names. I assumed
       | react would render that as native tags.
        
         | mlsarecmg wrote:
         | this is a custom renderer. "react" does not know what a "div"
         | is, this comes from "react-dom", that is why they have split
         | these two packages apart.
         | 
         | but they also allow you to make your own renderer (https://gith
         | ub.com/facebook/react/tree/main/packages/react-r...) which then
         | defines its own elements. you can try a mini threejs renderer
         | here: https://codesandbox.io/s/reurope-reconciler-hd16y
        
       ___________________________________________________________________
       (page generated 2021-08-15 23:00 UTC)