r-wasm / webr

The statistical language R compiled to WebAssembly via Emscripten, for use in web browsers and Node.
https://docs.r-wasm.org/webr/latest/
Other
805 stars 54 forks source link

how to access Module.webr.canvas #392

Closed bugzpodder closed 3 months ago

bugzpodder commented 3 months ago

tried import { Module } from "webr/emscripten";

got an error that its not exported. Want to try out Module.webr.canvas

also, didn't realize plotting ggplot2 is different now: we were using

options(device=webr::canvas)
plot(ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point())

but webR.flush() no longer flush out canvas messages

I think now I have to do:

webr::canvas()
plot(ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point())

to get webR.flush() to work as before

georgestagg commented 3 months ago

Module.webr.canvas exists only in the worker thread, not in the main thread. Since webR's JS worker thread is not a JavaScript module it's not possible to import the Module object directly in that way.

I am thinking about ways to make JavaScript development with the worker thread easier, by exposing the internals as a JavaScript module so that custom worker scripts can be produced with tools like esbuild or webpack, but the work has not yet been done and still requires some thought.

For the moment the worker thread internals should be considered as a private API that may change at any time, but nevertheless it is possible to gain access to the Module object through webR's webr::eval_js() mechanism, which runs JS in the worker thread context.

webr::eval_js('console.log(Module.webr.canvas)')

Unfortunately, eval_js() is only able to return integers back to R right now (we do plan to expand that in webR v0.4.0). So, even once you have access to Module you'll need to send JS objects you're interested in back to the main thread through the message queue:

webr::canvas(capture = TRUE)
plot(1)
webr::eval_js('
  chan.write({
    type: "mycanvas",
    data: Module.webr.canvas["1"].offscreen.transferToImageBitmap()
  })
')

But, be aware that this way of working is very tricky right now. Again, I hope to improve this in the future.


I am unable to reproduce the problem with ggplot. Does it work if you explicitly set the graphics device not to capture output?

options(device = function(...) { webr::canvas(capture = FALSE, ... ) })
plot(ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point())

That should be the default state, but perhaps something is going wrong.

bugzpodder commented 3 months ago

Thanks george, much appreciated!

I spent a bunch of time repo-ing options just now and couldn't do it. I think this is what happened:

I was manually testing webr::canvas(capture=TRUE), but did not refresh the page and then ran

options(device=webr::canvas)
plot(ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point())

and nothing happened. I switched to:

webr::canvas()
plot(ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point())

which probably reset capture=FALSE and got the results this way.

I will definitely give the worker stuff a try, but closing the issue since I have no further questions.