Skip to main content

Step 1 - Basic Setup: Creating the Card Grid

Let's set up the canvas and create our card grid with shuffled pairs!

Understanding the Card System

We'll create:

  • 16 cards in a 4×4 grid
  • 8 pairs (cards 1-8, each appearing twice)
  • Shuffled randomly so game is different each time
  • Each card is an object with properties like value, position, and state

Step 1: Create the Canvas

Open the p5.js Web Editor at https://editor.p5js.org/ and start with:

function setup() {
createCanvas(600, 600);
}

function draw() {
background(50);
}

Breaking it down

  • createCanvas(600, 600) - Square canvas for our square grid
  • background(50) - Dark gray background

Test it: Click play ▶️ - you should see a dark gray square!

Step 2: Add Grid Variables

Let's define our grid layout:

let cols = 4;
let rows = 4;
let cardWidth;
let cardHeight;
let cards = [];

function setup() {
createCanvas(600, 600);

// Calculate card dimensions
cardWidth = width / cols - 20; // Leave space for margins
cardHeight = height / rows - 20;
}

function draw() {
background(50);
}

Breaking it down

  • cols = 4 - 4 columns of cards
  • rows = 4 - 4 rows of cards (4×4 = 16 cards total)
  • cards = [] - Array to hold all card objects
  • cardWidth = width / cols - 20 - Canvas width (600) ÷ 4 columns = 150, minus 20 for spacing = 130px per card
  • cardHeight = height / rows - 20 - Same calculation for height

Step 3: Create Card Values

Let's create the pairs of cards:

let cols = 4;
let rows = 4;
let cardWidth;
let cardHeight;
let cards = [];

function setup() {
createCanvas(600, 600);

cardWidth = width / cols - 20;
cardHeight = height / rows - 20;

// Create card values (8 pairs)
let values = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];

console.log(values);
}

Breaking it down

  • values array has numbers 1-8, each appearing twice
  • These are the values we'll match
  • 8 unique numbers × 2 = 16 cards total

Test it: Check console - you should see the array of values!

Step 4: Shuffle the Cards

Cards should be in random positions each game:

function setup() {
createCanvas(600, 600);

cardWidth = width / cols - 20;
cardHeight = height / rows - 20;

// Create card values (8 pairs)
let values = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];

// Shuffle the values
values = shuffle(values);

console.log(values);
}

Breaking it down

  • shuffle(values) - p5.js function that randomly rearranges array
  • Now cards will be in different positions each game!
  • Run multiple times - notice console shows different order

Test it: Run several times - values change each time!

Step 5: Create Card Objects

Now let's create card objects with positions:

function setup() {
createCanvas(600, 600);

cardWidth = width / cols - 20;
cardHeight = height / rows - 20;

let values = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
values = shuffle(values);

// Create card objects
let index = 0;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
let card = {
value: values[index],
x: i * (cardWidth + 20) + 10,
y: j * (cardHeight + 20) + 10,
flipped: false,
matched: false
};
cards.push(card);
index++;
}
}

console.log("Created", cards.length, "cards");
}

Breaking it down

Card object structure:

{
value: 1, // The number on the card (1-8)
x: 10, // X position on canvas
y: 10, // Y position on canvas
flipped: false, // Is card currently face-up?
matched: false // Has card been matched with its pair?
}

Creating cards:

  • let index = 0 - Track which value from shuffled array
  • for (let i = 0; i < cols; i++) - Loop through columns (0-3)
  • for (let j = 0; j < rows; j++) - Loop through rows (0-3)
  • x: i * (cardWidth + 20) + 10 - Position calculation:
    • Column 0: 0 × 150 + 10 = 10
    • Column 1: 1 × 150 + 10 = 160
    • Column 2: 2 × 150 + 10 = 310
    • Column 3: 3 × 150 + 10 = 460
    • Adds spacing between cards
  • y: j * (cardHeight + 20) + 10 - Same for Y position
  • cards.push(card) - Add card to array
  • index++ - Move to next value

Test it: Console should show "Created 16 cards"!

Step 6: Draw the Cards

Let's draw rectangles for each card:

function draw() {
background(50);

// Draw all cards
for (let card of cards) {
// Card background
fill(100, 150, 200);
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);
}
}

Breaking it down

  • for (let card of cards) - Loop through all card objects
  • fill(100, 150, 200) - Blue color for cards
  • stroke(255) - White border
  • strokeWeight(3) - Thick border (3 pixels)
  • rect(card.x, card.y, cardWidth, cardHeight, 10) - Draw rectangle
    • card.x, card.y - Position
    • cardWidth, cardHeight - Size
    • 10 - Rounded corners (radius of 10)

Test it: You should see a 4×4 grid of blue cards with rounded corners!

Step 7: Add Card Back Design

Let's add a pattern for face-down cards:

function draw() {
background(50);

// Draw all cards
for (let card of cards) {
if (card.flipped || card.matched) {
// Face-up: show value (we'll add this later)
fill(200, 100, 100);
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);
} else {
// Face-down: show card back
fill(100, 150, 200);
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);

// Add pattern to card back
stroke(80, 120, 160);
strokeWeight(2);
line(card.x + 10, card.y + 10,
card.x + cardWidth - 10, card.y + cardHeight - 10);
line(card.x + cardWidth - 10, card.y + 10,
card.x + 10, card.y + cardHeight - 10);
}
}
}

Breaking it down

  • if (card.flipped || card.matched) - Check if card is face-up
    • card.flipped - Currently being looked at
    • card.matched - Already found its pair
  • Face-up cards:
    • Red/pink color (200, 100, 100)
    • Will show value later
  • Face-down cards:
    • Blue color (100, 150, 200)
    • Draw X pattern with diagonal lines
    • Lines from corners create recognizable "back" design

Test it: All cards should show as face-down (blue with X pattern)!

Step 8: Display Card Values

Now let's show the numbers on face-up cards:

function draw() {
background(50);

// Draw all cards
for (let card of cards) {
if (card.flipped || card.matched) {
// Face-up: show value
fill(200, 100, 100);
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);

// Display the card value
fill(255);
noStroke();
textSize(40);
textAlign(CENTER, CENTER);
text(card.value, card.x + cardWidth / 2, card.y + cardHeight / 2);
} else {
// Face-down: show card back
fill(100, 150, 200);
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);

stroke(80, 120, 160);
strokeWeight(2);
line(card.x + 10, card.y + 10,
card.x + cardWidth - 10, card.y + cardHeight - 10);
line(card.x + cardWidth - 10, card.y + 10,
card.x + 10, card.y + cardHeight - 10);
}
}
}

Breaking it down

  • fill(255) - White text
  • noStroke() - No outline on text
  • textSize(40) - Large readable numbers
  • textAlign(CENTER, CENTER) - Center text both horizontally and vertically
  • text(card.value, card.x + cardWidth / 2, card.y + cardHeight / 2) - Display value at center of card

Step 9: Test with Flipped Cards

Let's temporarily flip some cards to test the display:

function setup() {
createCanvas(600, 600);

cardWidth = width / cols - 20;
cardHeight = height / rows - 20;

let values = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
values = shuffle(values);

let index = 0;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
let card = {
value: values[index],
x: i * (cardWidth + 20) + 10,
y: j * (cardHeight + 20) + 10,
flipped: false,
matched: false
};
cards.push(card);
index++;
}
}

// Test: flip first two cards
cards[0].flipped = true;
cards[1].flipped = true;
}

Breaking it down

  • cards[0].flipped = true - Flip first card
  • cards[1].flipped = true - Flip second card
  • Should see two cards face-up with their numbers

Test it: You should see 2 red cards with numbers, rest are blue!

Step 10: Remove Test Code

Remove the test flips so we start with all cards face-down:

function setup() {
createCanvas(600, 600);

cardWidth = width / cols - 20;
cardHeight = height / rows - 20;

let values = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
values = shuffle(values);

let index = 0;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
let card = {
value: values[index],
x: i * (cardWidth + 20) + 10,
y: j * (cardHeight + 20) + 10,
flipped: false,
matched: false
};
cards.push(card);
index++;
}
}
// Test code removed - all cards start face-down
}

Complete Step 1 Code

Here's everything together:

let cols = 4;
let rows = 4;
let cardWidth;
let cardHeight;
let cards = [];

function setup() {
createCanvas(600, 600);

cardWidth = width / cols - 20;
cardHeight = height / rows - 20;

let values = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
values = shuffle(values);

let index = 0;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
let card = {
value: values[index],
x: i * (cardWidth + 20) + 10,
y: j * (cardHeight + 20) + 10,
flipped: false,
matched: false
};
cards.push(card);
index++;
}
}
}

function draw() {
background(50);

for (let card of cards) {
if (card.flipped || card.matched) {
fill(200, 100, 100);
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);

fill(255);
noStroke();
textSize(40);
textAlign(CENTER, CENTER);
text(card.value, card.x + cardWidth / 2, card.y + cardHeight / 2);
} else {
fill(100, 150, 200);
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);

stroke(80, 120, 160);
strokeWeight(2);
line(card.x + 10, card.y + 10,
card.x + cardWidth - 10, card.y + cardHeight - 10);
line(card.x + cardWidth - 10, card.y + 10,
card.x + 10, card.y + cardHeight - 10);
}
}
}

What You Learned

In this step, you:

  • ✅ Created a 600×600 canvas
  • ✅ Set up a 4×4 grid layout
  • ✅ Created 16 card objects with values 1-8 (8 pairs)
  • ✅ Shuffled cards randomly
  • ✅ Stored card properties (value, position, flipped, matched)
  • ✅ Drew cards with rounded corners
  • ✅ Added face-down pattern (X design)
  • ✅ Displayed card values when face-up
  • ✅ Learned about object properties

What's Next?

We have beautiful cards arranged in a grid, but we can't interact with them yet! Next, we'll add click detection so players can flip cards.

Coming up: Detecting clicks and flipping cards!


Next Step: Step 2 - Flipping Cards: Click Detection