Cant call from an Array on createGraphics canvas

Project Goal: I’d like to create a simple drawing program that randomly cycles through an array of shapes which the user has to place one at a time. I have a simple version built but when I try to draw something from an array onto my createGraphics canvas I get a TypeError “Cannot read properties of undefined (reading ‘0’)”.

I’m using createGraphics so that I can see where the shape will be drawn before clicking the mouse to draw it onto the canvas. Is there a better approach for this or am I just making a small error with the arrays, variables, or syntax?

Problem Area
illustration.circle(mouseX, mouseY, 50);
illustration.shapes0;

let illustration;
let shapes = [s1];

function setup() {
  createCanvas(400, 400);
  illustration = createGraphics(width, height);
  illustration.clear();
}

function draw() {
  background(0);
  shapes[0]();
  
  image(illustration, 0, 0);
}

function mousePressed() {
  illustration.noFill();
  illustration.stroke(255);
  illustration.circle(mouseX, mouseY, 50);
  illustration.shapes[0]();
}

function s1() {
  noFill();
  stroke(255);
  circle(mouseX, mouseY, 50);
}
}```

It’s not clear why you think this would work:

illustration.shapes[0]();

Nor why you think this would be able to draw to both the main canvas and your p5.Graphics:

function s1() {
  noFill();
  stroke(255);
  circle(mouseX, mouseY, 50);
}

Even if you fixed the fact that illustration.shapes is undefined by doing something like illustration.shapes = shapes; this still wouldn’t do what you want. All of the functions referenced in the s1 function (noFill, stroke, and circle) are global functions that draw to the main canvas. In order to change this you would need to make the function support specifying an explicit target:

function s1(g) {
  if (!g) {
    g = window;
  }
  g.noFill();
  g.stroke(255);
  g.circle(mouseX, mouseY, 50);
}

Then when you call your shape functions, specify the target as an argument:

shapes[0](illustration);

Thanks KumuPaul. I appreciate the feedback.

Maybe this a more direct way to ask the question.
I want the functionality from the new sketch below but I want to be able to see the shapes on the canvas before I click to place them. Should I be using the createGraphics approach for a transparent layer to draw on or is there a better way to go about it?

let shapes = [s1, s2, s3];
let index = 0;

function setup() {
  createCanvas(400, 400);
  background(0);
}

function draw() {
}

function mousePressed() {
  shapes[index]();
  index++;
  if (index >= shapes.length){
    index = 0;
  }
}

function s1() {
  circle(mouseX, mouseY, 75);
}

function s2() {
  square(mouseX, mouseY, 50);
}

function s3() {
  ellipse(mouseX, mouseY, 50, 200);
}

One possible approach:

let shapes = [s1, s2, s3];
let pg;
let sel;
let selection = 0;
let _x = 0;
let _y = 80;
let _w = 400;
let _h = 320;

function setup() { 
  createCanvas(400, 400);
  pg = createGraphics(50,50);
  background(209);
  textAlign(CENTER);
  sel = createSelect();
  sel.position(80, 10);
  sel.option('Shapes...');
  sel.option('circle');
  sel.option('square');
  sel.option('ellipse');
  sel.changed(selectShape);  
}

function draw() {
}

function mousePressed() {
  if(mouseX >= _x && mouseX <= _x + _w && mouseY >= _y && mouseY <= _y + _h){
   shapes[selection]();
  }
}

function selectShape(){
  let item = sel.value();
  switch(item){
    case 'circle': previewCircle(); selection = 0; break;     
    case 'square': previewSquare(); selection = 1; break;     
    case 'ellipse': previewEllipse(); selection = 2; break;     
  }
}

function previewCircle(){
  pg.background(255);
  pg.circle(25,25,25);
  image(pg,0,0);
}

function previewSquare(){
  pg.background(255);
  pg.square(5,5,25);
  image(pg,0,0);
}

function previewEllipse(){
  pg.background(255);
  pg.ellipse(25,25,25,50);
  image(pg,0,0);
}

function s1() {
  fill(255, 0, 0);
  circle(mouseX, mouseY, 75);  
}

function s2() {
  fill(0, 255, 0);
  square(mouseX, mouseY, 50);
}

function s3() {
  fill(0, 0, 255);
  ellipse(mouseX, mouseY, 50, 200);
}

I think your approach of using a separate p5.Graphics object to draw the shapes to on click is perfectly reasonable. The important thing is to understand the fundamentals of JavaScript: assigning a function to an object and then invoking it does not change the meaning of global symbols, it only changes the meaning of the this keyword. Which is why in my code sample I suggest passing the p5.Graphics as an argument to your functions.