How to send data from Javascript to Streamlit (using P5js in Streamlit)

I need to include a P5js sketch in Streamlit, but after trying to create a custom component out of it (and failing miserably) I surrender.

It is relatively easy to include a P5js sketch, but it cannot return data to Streamlit (my own workaround: Including a js file with Streamlit - #4 by Nemecsek).

Alas I need now bidirectional communication between P5js and Streamlit, and I thought to implement another trick: P5js can write any data into a hidden <DIV>. Is it possible for Streamlit to read back the content of a <DIV> and rerun on change?

This would allow me to pass data between the two.

Found an easy workaround using Streamlit Bridge

After including the component in Streamlit app and instantiating a bridge, JS can send data to Streamlit that will be rerun automatically using window.parent.stBridges.send('my-bridge', values); where my-bridge is the bridge id defined in Streamlit.

Here a working example:

main.py:

import streamlit as st

import streamlit.components.v1 as components
from st_bridge import bridge

def p5js_sketch(sketch_file, js_params=None, height=200, width=200):
    sketch = '<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.min.js"></script>'
    
    sketch += '<script>'
    if js_params:
        sketch += js_params + "\n"
    sketch += open(sketch_file, 'r', encoding='utf-8').read()
    sketch += '</script>'
    components.html(sketch, height=height, width=width)
    
st.header("This is my P5js sketch")
st.text("Click somewhere")

p5js_sketch(
    sketch_file="sketch.js",
    js_params="""
        // sketch params are embedded at the beginning of the sketch without any change.
        const WIDTH=200;
        const HEIGHT=200;
        const BACKGROUND_COLOR='red';
        CIRCLE_COLOR='yellow';
        CIRCLE_SIZE=30;
    """,
    width=250,  # a little bigger than the sketch canvas
    height=250,
)

data = bridge("my-bridge", default="no value yet!")
st.write(data)

sketch.js:

// PARAMS will be embedded here

function setup() {
    createCanvas(WIDTH, HEIGHT);
    stroke('black');
    fill(CIRCLE_COLOR);
}

function draw() {
    background(BACKGROUND_COLOR);
    circle(mouseX, mouseY, CIRCLE_SIZE);
}

function mouseClicked() {
    // coordinates are passed to Streamlit
    let values = {
        x: mouseX,
        y: mouseY
    }
    window.parent.stBridges.send('my-bridge', values);
}

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.