Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: add ability to handle onmessage in worker #10

Open
wmaurer opened this issue May 15, 2023 · 3 comments
Open

Feature request: add ability to handle onmessage in worker #10

wmaurer opened this issue May 15, 2023 · 3 comments

Comments

@wmaurer
Copy link

wmaurer commented May 15, 2023

I'd like to be able to handle onmessage in the worker. The react-three-offscreen render function already handles onmessage. I'd like to be able to hook into this.

(My use case is that I'm sending ImageBitmaps from the video camera to the worker which are passed on to the Scene component as a stream)

As an experiment, I've changed the signature of render to accept a callback:

function render(children: React.ReactNode, onmessageHandler: (event: MessageEvent) => void);

I then call the callback inside render's handler:

    self.onmessage = event => {
        const { type, payload } = event.data;
        const handler = handlerMap[type as keyof typeof handlerMap];
        if (handler) handler(payload);
        onmessageHandler(event);
    };

Would you accept a PR for this feature request?

@wmaurer wmaurer changed the title Feature request: add ability handle onmessage in worker Feature request: add ability to handle onmessage in worker May 15, 2023
@austintheriot
Copy link

austintheriot commented Oct 25, 2023

First off: thank you for making this library. I think it's a fantastic idea, and now that OffscreenCanvas is supported in all major browsers (check out latest support in Safari), you'll likely be getting more traction here.

As @wmaurer mentioned, it would be nice to be able to hook into the onmessage handler.

Any users interested in using this package are likely doing so because they want/need every ounce of performance they can get in the browser. postMessage on WebWorkers is a crucial part of web performance, because it allows developers to performantly send transferrable objects with very little overhead, which is not possible to do with the BroadcastChannel api. Only allowing users to use BroadcastChannel seems like the wrong choice here.

@austintheriot
Copy link

Note for any others: after looking more, it's also possible to use transferable objects via the MessagePort object: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage

^ This may in fact be the best way to interact with your rendering WebWorker, since you are able to "transfer" objects that exist in the same thread OR in different threads, allowing you to isomorphically interact with your rendering thread/scene

@j-chmielewski
Copy link

j-chmielewski commented Sep 16, 2024

Hi, I'm late to the party but I came up with a solution that worked for me so I thought I'd leave it here:

// worker.jsx

import React from "react";
import { render } from "@react-three/offscreen";
import Scene from "./Scene";
import mitt from "mitt";

// emitter to pass coords into our Scene component
const emitter = mitt();

// render method sets up worker `onmessage` handler
render(<Scene emitter={emitter} />);

// save the `onmessage` handler
const offscreenMessageHandler = self.onmessage || function () {};

// implement our own `onmessage` handler with routing to avoid
// intercepting offscreen stuff
onmessage = (event) => {
  const { type, payload } = event.data;
  if (type === "coords") {
    // pointer coords, pass them to the Scene with mitt emitter
    emitter.emit("coords", payload);
  } else {
    // react-three-offscreen messages, pass them to original handler
    offscreenMessageHandler(event);
  }
};
// Scene.tsx
interface SceneProps {
  emitter: Emitter<Event>;
}

function Scene({ emitter }: SceneProps) {
  const [coords, setCoords] = useState<Coords | null>(null);

  useEffect(() => {
    if (emitter) emitter.on("coords", (newCoords) => setCoords(newCoords));
  }, [emitter]);

...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants