Skip to content

Add http backend#202

Open
almarklein wants to merge 21 commits into
mainfrom
http
Open

Add http backend#202
almarklein wants to merge 21 commits into
mainfrom
http

Conversation

@almarklein

@almarklein almarklein commented Mar 26, 2026

Copy link
Copy Markdown
Member

A backend that runs a webserver that you can connect to with your browser. Multiple clients are supported (eventually).

Using ASGI, so it can run on any ASGI server and we don't have any hard dependencies 🚀

  • ASGI server.
  • Websocket connection.
  • Implement JS to be a AFM (anywidget front-end module) host. Or JS to control the renderview directly.
  • Python backend module.
  • Support for multiple simultaneous clients.
  • Support for including this in a larger web app, e.g. FastAPI.
  • Allow customizing the html and e.g. add a logo.
  • docs
  • Cleanup

WIP AFM host code, saving here for reference, but I think I'll skip the afm:

        import {default as afm_factory} from './renderview-afm.js'
        const afm = afm_factory();

        // Open websocket
        const ws = new WebSocket("ws://" + location.host + "/ws");

        ws.onopen = (e) => {


        };
        ws.onerror = (e) =?
        ws.onmessage = (e) => {
            let msg = e.data
            if msg.
        }
        ws.onclose = (e) => {
        }

        const callbacks = {}
        const store = {"css_height"}
        let storeChanges = {}

        class AfmModel {
            on (eventName, callback) {
                callbacks[eventName] |= []
                callbacks[eventName].push(callback)
            }
            get (key) {
                return store[key]
            }
            set (key, value ) {
                store[key] = value
                storeChanges[key] = value
            }
            save_changes () {
                ws.send({'type': 'storeChanges', 'data': storeChanges})
                storeChanges = {}
            }
            send (ob) {
                ws.send(ob)
            }
        }

        const model = AfmModel();
        const el = document.getElementById('canvas')

        afm.initialize({model});
        afm.render({el})

@almarklein

almarklein commented Apr 9, 2026

Copy link
Copy Markdown
Member Author

Progress! You can basically replace one line to say from rendercanvas.http import RenderCanvas, loop, and run the same code, but now its a webserver 🚀

image

@almarklein

Copy link
Copy Markdown
Member Author

This is nearly done. Need some cleanup, and resolving some small todos. The server can take multiple clients, who each see the same rendered image. The stream is currently throttled by the slowest connection. Only one client is active: it determines the size of the canvas, and the controls the mouse etc.

Comment thread rendercanvas/core/renderview.js
@almarklein almarklein marked this pull request as ready for review June 24, 2026 13:01
@almarklein

Copy link
Copy Markdown
Member Author

A review would be great. Otherwise I'll just merge this in a few days.

viewElement.addEventListener('pointerdown', (ev) => {
// When pointer is down, set focus to the focus-element.
if (!LOOKS_LIKE_MOBILE) {
this._focusElement.focus({ preventScroll: true, focusVisble: false })

@Vipitis Vipitis Jun 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this._focusElement.focus({ preventScroll: true, focusVisible: false })

another potential upstream bug?

Comment thread rendercanvas/http.py
It is assumed that there is exactly one canvas per connected client.
Multiple clients can simultaneously connect to the server. They will be served
the same stream of images. There is one "active" client, which determines
the pase of rendering. Events from the passive clients are ignored.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the pase of rendering. Events from the passive clients are ignored.
the pace of rendering. Events from the passive clients are ignored.

Comment thread examples/cube_http.py


# the loop.run() of this backend uses uvicorn to start a webserver
loop.run()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe document that loop.run(host="address", port:1234) takes two kwargs. Potentially the base loop .run should consume kwargs for portability?

@Vipitis Vipitis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no webdev experience, so I can't give any input on the core changes.

Managed to try it even with multiple clients. I am not sure what the timeout is, but if the active client tabs away it basically freezes the rendering - but it remains the active client.

Should there be some notice about the bidirectional nature by default? Because you might receive events from untrusted clients that could somehow be malicious (I am not sure how this would be exploited beyond a denial of service, by like requesting really large resolutions for example? or maybe a close event? - people will find a way).

finally an idea: what might be possible when running multiple backends at the same time? For example one "presenter" doing it locally while others observe via http (or like a frame server that encodes a video file to save/stream it)

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

Successfully merging this pull request may close these issues.

2 participants