Use an existing canvas with p5js

Hi,
I would like to use an existing canvas with p5js. It seems not possible in a simple way.
The reason I want to do that is: I don’t want p5js to insert a canvas and make my already displayed page to suddenly extend. This will generally not happen because the script will start quickly, but I have no guarantee this won’t happen to someone whose browser takes too much time to download the p5j library. I hate it when a page extends itself when I am browsing it, and I think such behavior should not exist.
How would you solve this problem?
Thanks for your answer.

I couldn’t find a way to connect an existing canvas to p5js drawings, but if your concern is the page shifting due to late loading of the library, you can specify a parent HTML element of the p5js canvas. Say, create an empty div the same size as the p5js canvas first:

here is a simple example I made:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.1.9/lib/p5.js"></script>
    <style>
        #container {
            width: 400px;
            height: 400px;
            border: 1px solid green
        }
    </style>

</head>
<body>
    <div id='container'></div>
    <script>
        let sketch = p => {
            p.setup = () => {
                p.createCanvas(400, 400)
                p.background(220)
            }
        }

        // simulate a delayed loading of the sketch
        setTimeout(() => {
            new p5(sketch, document.querySelector('#container'))
        }, 2000)
    </script>
</body>
</html>
1 Like

Thank you, this works well. I like the delayed loading simulation part.

Previously I tried something similar for the container, but instead of using a fixed width and height, I tried width: 100% and padding-bottom: 56.25%, to create a container with ratio 16:9 filling its parent (and therefore automatic width and height). It worked for the container, but failed when the canvas was inserted (because the container grew).
I guess I’ll use fixed width and height as in your example, now that I know it works, but do you know why the following give a strange result? (e.g. the container grows after the canvas insertion):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.1.9/lib/p5.js"></script>
    <style>
        #container {
            width: 100%;
            padding-bottom: 56.25%;
            border: 1px solid green
        }
    </style>

</head>
<body>
    <div id='container'></div>
    <script>
        let container = document.querySelector('#container')

        let sketch = p => {
                p.setup = () => {
                p.createCanvas(container.clientWidth, container.clientHeight)
                p.background(220)
            }
        }

        // simulate a delayed loading of the sketch
        setTimeout(() => {
            new p5(sketch, container)
        }, 2000)
    </script>
</body>
</html>

Sorry I found the problem. I should remove the container padding before inserting.
container.style.paddingBottom = '0'
I shouldn’t copy paste code I don’t understand from the internet.

No worries.
The parent padding trick is an interesting one to use to keep the same aspect ratio,
and for this to work, it needs a few more lines of CSS.

I think this will solve the immediate issue of resizing the p5 canvas, but I also see some potential issues such as the canvas getting too large and heavy.

It might be better to keep the canvas size fixed to 16:9, say, 960x540 and just scale it up and down without increasing the pixel count.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.1.9/lib/p5.js"></script>
    <style>
        #container {
            width: 100%;
            padding-bottom: 56.25%;
            border: 1px solid green;
            position: relative;
        }

        #container>canvas {
            position: absolute;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
        }
    </style>

</head>

<body>
    <div id='container'></div>
    <script>
        let container = document.querySelector('#container')

        let sketch = p => {
            p.setup = () => {
                p.createCanvas(container.clientWidth, container.clientHeight)
            }
            p.draw = () => {
                p.background(220)
                p.ellipse(p.width / 2, p.height / 2, 200, 200)
            }
            p.windowResized = () => {
                p.resizeCanvas(container.clientWidth, container.clientHeight)
            }
        }

        // simulate a delayed loading of the sketch
        setTimeout(() => {
            new p5(sketch, container)
        }, 1000)
    </script>
</body>

</html>

Ok I got it.
thank you very much.