How I'm using GitHub Copilot to learn p5.js

How I'm using GitHub Copilot to learn p5.js

My manifesto of AI-Driven Learning

Introduction

Because I’m an avid advocate for GitHub Copilot and other forms of AI-assisted programming, people often ask me the same questions: “Does GitHub Copilot stunt learning? Will it limit my ability to learn code? Will I become overly reliant on it?” Concerns about stunted learning, limited coding abilities, and over reliance are valid. However, based on my experience, intentional use of GitHub Copilot has actually sharpened my coding skills. I’m an experienced JavaScript developer who used GitHub Copilot to gain proficiency in reading, writing, and debugging Python. While I successfully combined my existing coding expertise with GitHub Copilot's code generation abilities, I am not great at verbally explaining how I used it as a learning tool. In conversations with developer friends and even in a recent conference talk, I felt I could not deliver convincing evidence that this works for me and can work for others, too! My inability to express my thoughts makes me feel like I’m mindlessly advocating for a company’s product just because I work there. I want to demonstrate its value as a learning tool rather than a crutch to empower developers. Generative AI is here to stay and evolve whether we like it or not. We can either cower in fear or learn to leverage it for the well-being of society.

In this blog post, I aim to articulate this methodology effectively. Writing helps me organize my thoughts, so I'm sharing my insights with the hope of benefiting others. While the primary reason I write is for myself, I also believe that using my company's tools and sharing best practices is part of effective Developer Advocacy. By understanding how GitHub Copilot can be responsibly used as a learning tool, readers can explore more use cases for AI-assisted programming. Throughout this post, I will illustrate how to use GitHub Copilot to learn a programming language or framework, with a focus on my current experience with p5.js.

What is p5.js and why am I learning it?

P5.js is an open source JavaScript library for creative coding, with a focus on making coding accessible and inclusive.

I opted to learn p5.js as a means to rekindle my dormant creative side. Ever since I learned how to code, I stopped tapping into my creative side. I’ve learned that the purpose of creative coding is to make something expressive instead of something functional, and I need that in my life.I aspire to create purely expressive works, unburdened by the need to cater to a broad audience or meet functional demands. It's liberating to code free from concerns such as fixing bugs for a company's financial impact or facing potential criticism on blog posts about the usefulness of my demos.

What is GitHub Copilot?

It's an AI pair programmer that draws context from comments and code and suggests individual lines and whole functions instantly!

Here are some past posts I've written about GitHub Copilot if you want to learn more:

Here are some other informative posts about GitHub Copilot I didn't write:

How I learn programming concepts

Before we get into how we use GitHub Copilot to learn, I want to debunk some misconceptions about learning programming concepts. From my experience, developers tend to have a shared conventional way that people should learn to code that may make them rebuttal the way that GitHub Copilot helps you to learn. Everyone learns differently. Some people learn by doing, while some people learn through observation. However, I think there’s a difference between how we think we learn a programming language versus the reality of how we actually learn a programming language. Below are a few examples:

Expectation - Learning to code is straightforward

When I look back at my coding education, I remember the experience in a straightforward manner that looked something like:

  1. I listened to an instructor (college, bootcamp, or online video) introduce a concept

  2. I watched the instructor demonstrate the concept

  3. I applied what I learned from the class in my homework

Reality - Software engineering is a non-linear career path that involves various steps

The reality is coding is a continuous, non-linear journey. The “curse of knowledge” or “knowledge bias” often leads us to forget what learning code was really like. The curse of knowledge refers to the cognitive bias where individuals who possess knowledge or expertise in a particular subject find it difficult to remember or accurately assess the difficulty and challenges faced by novices or learners who are still acquiring that knowledge.

When I started teaching beginners to code, I sometimes felt frustrated. Why aren’t they quickly understanding the box model? I showed them a fun, but comprehensive illustration. They should get it.

Then, I realized how confusing learning the CSS Box Model was for me when I first learned to code. I remembered that there are senior engineers who still confuse padding with margin. And I remembered that I continue to struggle with other concepts every single day. (I still get confused with the useEffect hook and the Promise.all() method).

In practice, learning to code involves troubleshooting, reading documentation, copying code snippets, browsing Stack Overflow, and grappling with the frustration of code not working as expected during assignments. This aspect of the learning journey is often overlooked but is a common experience when mastering a new programming language or framework. Even today, when working with new technologies, I encounter similar frustrations.

Expectation - To get better at writing code, write more code.

Some of the most common advice for novice and experienced developers looking to improve their coding skills or to learn a new programming concept is to write more code

Reality - Improve your coding skills by reading code

Writing more code helps, but reading code is even better. My friend Ramón Huidobro introduced me to this book called The Programmer's Brain.

“What Every Programmer Needs to Know about Cognition" by Felienne Hermans. While Felienne acknowledges that writing code is important, she also emphasizes that our field doesn’t place enough importance or instruction on reading code. She believes in personalizing the learning process so that you can comprehend the code and improve your short-term, working, and long-term memory to become a better programmer. Watch one of Felienne’s presentations to learn more!

I believe that while you may not always understand snippets of code, it’s helpful to read code, so that you can recognize the notation and feel less overwhelmed by it over time.

Expectation - You have to understand why you write every single line of code

While I believe that code comprehension is key, I think there will always be times when we don’t understand code and that comprehension can grow over time. One of the biggest frustrations I had when I worked as a software engineer was that my coworkers wanted me to understand every single line of code that I wrote.

Them: Why did you write that? Me: I’m not sure. It’s something I saw online. I thought it was an interesting approach, but I don’t fully understand it. Them: Don’t just copy things you see online.

You might agree with my past coworkers, and what they’re saying holds some truth, but read the next paragraph to understand why I don’t fully agree with this mindset.

Reality - Copying code and procedures supports ritualistic learning

I’ve found that many engineers like the idea of knowing the why before the how. I, on the other hand, learn best from learning how to do something then after that I learn WHY I am doing something. It clicks better in my brain this way. Like I mentioned earlier, everyone learns differently.

Here’s an example: for 3 months straight from January 2018 to March 2018, I knew how to push code to GitHub, but I didn’t understand what any of the commands meant._ Why was I writing them? I had no clue. _I just knew I needed to type them in the terminal to get my code on GitHub. However, when I encountered problems, such as being unable to push due to lack of repository access or facing a merge conflict, I would Google the issues and gain more context about the solutions and how each command worked. After a few months, I learned the meaning of these commands and I knew how to navigate problems that came up around those commands. The approach didn’t inhibit my learning in any way because now I work at today’s most popular web-based hosting service for Git repositories.

This is not a made up way of learning. This approach is called ritualistic learning. It involves following a set of instructions or steps without necessarily comprehending the underlying principles or reasons behind them. Over time, as you gain more experience and exposure to the subject matter, your understanding gradually develops.

How GitHub Copilot helps me learn

For the sake of clarity (and me wanting to feel fancy), I’ll name the framework for using GitHub Copilot to learn a new programming concept: AI-Driven Learning. It incorporates 5 steps including:

  • Conceptual Pseudocoding

  • Syntax Familiarization

  • Iteration

  • AI Rubber Ducking

  • Self-Directed Application

Let’s dive into each of the steps to understand what they mean and how to apply them!

Conceptual Pseudocoding

I discovered the importance of conceptual pseudocoding while planning my segment for the GitHub Universe Keynote in 2022. Collaborating with Jonathan Carter, the Technical Advisor to the CEO, we aimed to create a fun and visual demonstration of GitHub Copilot that could fit within the allotted 5-minute timeframe.

During our Slack brainstorming session, I had an epiphany! Why not showcase GitHub Copilot's capabilities by creating an image with p5.js? Jonathan liked the idea, and we were all set. There was just one tiny hitch – I had never written anything in p5.js before. Why did I suggest it in the first place? But then, a glimmer of hope emerged. Maybe I could grasp it quickly? How hard could it be? Perhaps less than an hour of learning would suffice. And now that I’m looking at code samples of p5.js, I’m not sure why there are so many numbers. Is this math?

After a few attempts of exchanging screenshots and screen recordings with Jonathan, we developed a plan to provide a comment at the top of the file, providing GitHub Copilot with the context of what we wanted GitHub Copilot to build. It said:

/* 
Draw a  draw a green house, 
with a blue roof, 
a red door,
 and two windows 
*/

This helped me to break down the different elements of my p5.js creation.

And just like magic, GitHub Copilot followed all the instructions.

Green house with blue roof red door and two windwos

This process helped GitHub Copilot understand our desired outcome and helped me, the programmer, gain clarity on what I wanted to achieve. Although pseudocoding is often emphasized in college, coding bootcamps, and interviews, it tends to be overlooked once we enter the industry. By writing comments that prompt and provide context to GitHub Copilot, developers can consider the application's functionality and structure thoughtfully.

To illustrate this further, let's create our own example by installing p5.js and GitHub Copilot and writing a high-level prompt to draw an ice-cream cone:


/* draw a light brown ice cream cone with

- a scoop of light pink ice cream

- a red cherry on top

*/

Syntax Familiarization

After you write a comment or some code, GitHub Copilot may suggest code. We call this ghost text. You can accept the ghost text by pressing tab. While many developers acknowledge that tabbing through your code boosts your productivity, they’re concerned that not typing code will reduce their memory for syntax, especially for beginners.

Intentionality plays a key role here. Although you're not actively typing the code, allowing GitHub Copilot to generate it provides exposure to the syntax and typical file structure of the language. At this stage, complete understanding of the code is not necessary, but continual exposure helps prevent it from appearing unfamiliar. This exposure primes your brain to break down the code more effectively, aiding comprehension.

I’ll draw a parallel between understanding complex words and understanding code through syntax familiarization to best illustrate my point.

I’m not afraid of big or unfamiliar words. Perhaps, reading them aloud is not my strong suit, but my brain is able to process the meaning of the words pretty quickly, and that’s because I learned Latin in middle school. I can infer that a word like, “soporific” means to make you sleepy. The prefix first half of the word “sopor” is Latin for “deep sleep”. The second half of the word “fic” is Latin for “to make.” Readers of this blog post might even be able to infer this even if they’re not familiar with Latin. They may recognize the word “fic” from the word “fiction”, which also implies that you’re making or inventing something from your mind.

Just as my previous knowledge of Latin helps me decipher complex words based on prefixes and suffixes, familiarity with code syntax enables you to infer meaning and context. Syntax familiarization offers additional benefits by exposing developers to various programming concepts that can be applied across languages. While specific syntax may differ, underlying principles remain consistent. This exposure broadens developers' mindsets, encourages exploration of diverse approaches and solutions, and facilitates knowledge transfer.

Take a look at this GIF where GitHub Copilot generated code to draw an ice cream cone:

GIF where GitHub Copilot attempts to generate an ice cream cone with an ice cream scoop except the ice cream cone is facing the wrong direction

While GitHub Copilot didn’t generate a perfect ice cream cone, I learned a couple of things about the syntax and the structure of the file:

  • P5.js file start with a set up function

  • The set up function uses a method called createCanvas, which I can infer initializes the canvas for our creation

  • P5.js files also use a function called draw and this is where all the elements of our drawing will live

  • I can determine the background color by using the method background and passing in RGB color codes

  • I can create shapes like triangles, ellipses, and arcs by specifying the name of the shape and passing in numbers. Although, at the moment, I’m not exactly sure what the numbers do. I assume that they determine the size and position of the shapes.

  • I can color the shapes using the method fill and passing in RGB color codes

By intentionally engaging with syntax familiarization, developers can leverage their prior knowledge, make inferences, and establish meaningful connections between different programming concepts.

Iteration

During conversations with my developer friends, the issue of GitHub Copilot generating flawed code often arises. They argue that this can mislead beginners and teach them poor programming practices. I won’t lie. Mindlessly trusting incorrect code generative code CAN mislead beginners. However, I believe that encountering imperfect code is an integral part of the learning process, regardless of whether GitHub Copilot is involved. When exploring a new programming language, especially at the beginning of my career, I often resort to copying code from StackOverflow, skimming through documentation, and pasting code snippets haphazardly. Sometimes, the code I copy doesn't even work, but it still provides a starting point and helps me grasp the problem at hand. With time, I learn better practices and approaches, gradually refining my skills.

The same principle applies to GitHub Copilot. In the example above, GitHub Copilot attempted to generate an ice cream cone with a light pink scoop and a cherry on top. However, the generated triangle for the cone had the pointy part facing upwards, whereas cones typically have the pointy part facing downwards. While it could be argued that GitHub Copilot misled me, I wouldn't have known how to create a triangle without its guidance. This presents an opportunity for me to debug and iterate on the generated code, enabling me to learn and develop a deeper understanding.

Though I may not fully comprehend the numerical notation used, I can modify the values to achieve the desired result.

Check out the GIF below to see how I adjusted the values to flip the triangle in the desired direction:

GIF where I edit the coordinates of the triangle until it’s transformed from an upward pointing triangle to a downward pointing triangle

When GitHub Copilot offers code suggestions that contain minor errors or imperfections, it allows learners to adopt a hands-on approach. By attempting to execute the suggested code, identifying and resolving encountered issues, and subsequently refining the code to correct errors, learners gain practical experience in writing and editing within the given framework.

AI Rubber Ducking

With trial and error, I achieved the desired result, but I still don’t truly understand the numerical notation or why it worked. I can now see that the numbers for each triangle represent vertices, but I don’t know the formula. In this situation, I would love to have a conversation with someone more knowledgeable than me to get a better understanding, but I don’t want to bother anyone. Or maybe I can Google, but the search engine results don’t always have the answers for my specific use-case.

Fortunately, I have the option to engage in a conversation with my AI pair programmer using a tool called Copilot Chat. At GitHub, we have a suite of Copilot tools called Copilot X. These tools are made to help developers beyond their editor because we recognize developers do more than just code. One of the products within Copilot X is called Copilot Chat. Copilot Chat is an IDE extension that provides a ChatGPT-esque experience. It offers an IDE extension that provides a ChatGPT-esque experience, leveraging the context of your open files within the editor. From my experience, highlighting the relevant lines of code enhances Copilot Chat’s performance. Copilot Chat can generate code, unit tests, explanations, and more. Learn more about Copilot Chat’s latest and greatest updates for June 2023 here. Try out Copilot Chat’s slash commands to maximize its benefits! \

Slash commands in Copilot Chat

For our particular scenario, I’ll use Copilot Chat to help me better understand the generated p5.js code. I’ll ask clarifying questions like:

Can you explain this line of code to me? triangle(100, 200, 300, 200, 200, 400);

GIF of Copilot Chat explaining the triangle function to me

So is the method structured this way triangle(x1, y1, x2, y2, x3, y3)?

GIF of Copilot Chat confirming the order of arguments and vertices

How big is the canvas? How do I know where each coordinate is?

GitHub Copilot explaining the canvas size

From communicating with Copilot Chat, I’ve learned a few p5.js fundamentals:

  • The createCanvas(400, 400) method for defining the width and height of the canvas

  • The utilization of the Cartesian coordinate system in the canvas

  • The order of vertices for the triangle function: x1, y1, x2, y2, and x3, y3

  • The arc function's notation: arc(centerX, centerY, width, height, startAngle, stopAngle)

  • The syntax for creating ellipses: ellipse(centerX, centerY, width, height)

If you want to create this modify drawing, here’s the code:

/* draw a light brown ice cream cone with

- a scoop of light pink ice cream

- a red cherry on top

*/

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

function draw() {
    // light blue background

    background(200, 220, 255);

    // light brown ice cream cone

    fill(255, 200, 100);

    triangle(100, 200, 300, 200, 200, 400);

    // light pink ice cream half-circle

    fill(255, 200, 200);

    arc(200, 200, 200, 200, PI, TWO_PI);

    // red cherry

    fill(255, 0, 0);

    ellipse(200, 100, 30, 30);
}

With my newfound knowledge, I’m ready to challenge myself to create more!

Self-Directed Application

Now that I know how to draw shapes and control the position of shapes, especially triangles, I am ready to take on a more challenging task: drawing my parents' national flag. The Guyanese flag prominently features triangles of different sizes and colors.

Here’s my attempt at drawing a Guyanese flag

Drawing of the Guyanese flag

Here’s the code for the flag:

// draw a Guyanese flag

function setup() {
    createCanvas(700, 400);
}

function draw() {
    // background is green
    background(0, 153, 0);
    stroke(0);
    strokeWeight(2);
    flag();
}

function flag() {
    yellowTriangle();
    redTriangle();
}

function yellowTriangle() {
   // yellow triangle and white outline
   stroke(255, 255, 255);
   strokeWeight(4);
   fill(255, 255, 0);
   triangle(0, 400, 0, 5, 715, 200);
}

function redTriangle() {
    // red triangle and black outline
    stroke(0, 0, 0);
    strokeWeight(7);
    fill(255, 0, 0);
    triangle(0, 400, 0, 5, 350, 200);
}

I continued to apply my knowledge of shape manipulation to build a snowman with a top hat, a carrot nose, and a few buttons.GitHub Copilot and Copilot Chat came in handy as I explored animation and user interaction. This allowed me to add a background with randomly generated green squares, and now users can draw random brown lines by dragging their mouse across the canvas.

Check out my snowman below!

GIF of a snowman with a background that has green generative squares

Here’s the code to leverage for your own modification purposes:

// draw a snowman

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

function draw() {
    // background is dark blue

    // background(0, 0, 102);

    stroke(0);

    strokeWeight(2);

    snowman();
}

function snowman() {
    changeBackground();

    // call all functions

    body();

    eyes();

    nose();

    buttons();

    hat();

    arms();
}

function body() {
    // snowman body
    fill(255, 255, 255);
    ellipse(200, 300, 150, 150);
    ellipse(200, 200, 100, 100);
    ellipse(200, 120, 75, 75);
}

function eyes() {
    // snowman eyes
    fill(0, 0, 0);
    ellipse(185, 110, 10, 10);
    ellipse(215, 110, 10, 10);
}

function nose(){
    // snowman nose
    fill(255, 102, 0);
    triangle(200, 120, 200, 130, 220, 125);
}

function buttons() {
    // snowman buttons
    fill(250, 0, 0);
    ellipse(200, 180, 10, 10);
    ellipse(200, 200, 10, 10);
    ellipse(200, 220, 10, 10);
}

function hat() {
    // snowman hat
    fill(250, 0, 0);
    rect(150, 65, 100, 20);
    rect(170, 33, 60, 30);
}

// create snowman arms

function arms() {
    stroke(102, 51, 0);
    strokeWeight(5);
    line(150, 200, 100, 150);
    line(250, 200, 300, 150);

}

function changeBackground() {
    // make the background be random green squares
    for (var i = 0; i < 100; i++) {
        fill(0, 255, 0);
        rect(random(400), random(400), random(50), random(50));
        frameRate(2);
    }
  }

  // draw with mouse

function mouseDragged() {
    fill(0);
    ellipse(mouseX, mouseY, 10, 10);
    return false;
}

Building on my understanding of shape manipulation and animation, I embarked on creating a 3D drawing without a specific plan or goal in mind. I let the generative nature of p5.js guide me, focusing on developing elements in 3D, adding shadows, and incorporating lighting effects.

Here’s my 3D creation. It’s giving space!

Check out the code below:

// create a 3d sphere with WEBGL and p5.js

let angleX = 0;

let angleY = 0;

function setup() {
    createCanvas(400, 400, WEBGL);
}

function draw() {
    // background is dark blue
    background(0, 0, 102);
    // set up lighting
    ambientLight(60, 60, 60);
    pointLight(255, 255, 255, 80, -100, 100);
    // set up material properties
    specularMaterial(250);
    shininess(20);
    rotateX(angleX);
    rotateY(angleY);
    // draw 3D sphere
    noStroke();
    fill(255, 204, 0);
    sphere(100);
    // draw 3D torus
    noFill();
    stroke(255, 204, 0);
    torus(200, 200);
    angleX += 0.01;
    angleY += 0.02;
}

As a culmination of the previous steps, I am now able to apply the acquired knowledge and concepts independently while coding. Through repeated engagement with pseudocoding, syntax familiarization, iteration, and AI rubber ducking, I have developed an intuitive understanding of foundational knowledge in p5.js.

Conclusion

Whether you're a novice developer or an experienced professional, the journey of learning and mastering programming is a constant process of growth. It's important to recognize that learning is rarely a linear path and often involves various cognitive methods that are often overlooked. From copying and pasting code snippets to exploring new concepts, these approaches play a significant role in our development as programmers. By intentionally embracing AI tools like GitHub Copilot and other AI pair programming tools, we can enhance our learning experience and leverage the power of generative AI.

To recap, the AI-Driven Learning involves:

  • conceptual pseudocoding,

  • syntax familiarization,

  • iteration,

  • AI rubber ducking,

  • and self-directed application.

Try out GitHub Coplot, try out my approach, and share your thoughts about shaping the future of programming and redefining how we think about learning and programming.

Did you find this article valuable?

Support Black Girl Bytes by becoming a sponsor. Any amount is appreciated!