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 gridbackground(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 cardsrows = 4- 4 rows of cards (4×4 = 16 cards total)cards = []- Array to hold all card objectscardWidth = width / cols - 20- Canvas width (600) ÷ 4 columns = 150, minus 20 for spacing = 130px per cardcardHeight = 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
valuesarray 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 arrayfor (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 positioncards.push(card)- Add card to arrayindex++- 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 objectsfill(100, 150, 200)- Blue color for cardsstroke(255)- White borderstrokeWeight(3)- Thick border (3 pixels)rect(card.x, card.y, cardWidth, cardHeight, 10)- Draw rectanglecard.x, card.y- PositioncardWidth, cardHeight- Size10- 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-upcard.flipped- Currently being looked atcard.matched- Already found its pair
- Face-up cards:
- Red/pink color (
200, 100, 100) - Will show value later
- Red/pink color (
- Face-down cards:
- Blue color (
100, 150, 200) - Draw X pattern with diagonal lines
- Lines from corners create recognizable "back" design
- Blue color (
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 textnoStroke()- No outline on texttextSize(40)- Large readable numberstextAlign(CENTER, CENTER)- Center text both horizontally and verticallytext(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 cardcards[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