text:
# Object-oriented JavaScript marking guide

The aim of the tasks is to demonstrate an understanding of the JavaScript features covered in the [Object-oriented JavaScript for beginners](https://wiki.developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS), [Object prototypes](https://wiki.developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes), and [Inheritance in JavaScript](https://wiki.developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance) lessons in Learn Web Development on MDN.

Note: If there is an error in your code, it will be outputted into the results panel on the page, to help you try to figure out the answer (or into the browser's JavaScript console, in the case of the downloadable version).

## Task 1

In this task we provide you with a `Shape` class containing only some data properties.

We want you to:
* add a constructor
* define a `calcPerimeter()` method
* create two instances of the class, a square and a triangle, and call their `calcPerimeter()` methods

Your code should look something like this:

```
class Shape {

  name;
  sides;
  sideLength;

  constructor(name, sides, sideLength) {
    this.name = name;
    this.sides = sides;
    this.sideLength = sideLength;
  }

  calcPerimeter() {
    console.log(`The ${ this.name }'s perimeter length is ${ this.sides * this.sideLength }.`);
  }

}

const square = new Shape('square', 4, 5);
square.calcPerimeter();

const triangle = new Shape('triangle', 3, 3);
triangle.calcPerimeter();
```

## Task 2

Next, we'd like you to start with the `Shape` class you created in the last task.

We'd like you to create a `Square` class that inherits from `Shape`, and adds a `calcArea()` method that calculates the square's area.

Create an instance of the `Square` called square with appropriate property values, and call its `calcPerimeter()` and `calcArea()` methods to show that it works.

Your code should look something like this:

```
class Shape {

  name;
  sides;
  sideLength;

  constructor(name, sides, sideLength) {
    this.name = name;
    this.sides = sides;
    this.sideLength = sideLength;
  }

  calcPerimeter() {
    console.log(`The ${ this.name }'s perimeter length is ${ this.sides * this.sideLength }.`);
  }

}

class Square extends Shape {

  constructor(sideLength) {
    super('square', 4, sideLength);
  }

  calcArea() {
    console.log(`The ${ this.name }'s area is ${ this.sideLength * this.sideLength } squared.`);
  }

}

const square = new Square(4);

square.calcPerimeter();
square.calcArea();
```


text:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>OOJS: Task 1</title>
    <style>
      p {
        color: purple;
        margin: 0.5em 0;
      }

      * {
        box-sizing: border-box;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>

    <section class="preview">

    </section>

  </body>
  <script>
  class Shape {

    name;
    sides;
    sideLength;

  }

  </script>

</html>

repo_name: mdn/learning-area

repo_language: HTML

mime_type: text/plain


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>OOJS: Task 1</title>
    <link rel="stylesheet" href="../styles.css" />

    <textarea class="playable playable-js" style="height: 220px;">
class Shape {

  name;
  sides;
  sideLength;

}
    </textarea>

      <input id="reset" type="button" value="Reset" />


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>OOJS: Task 2</title>
    <link rel="stylesheet" href="../styles.css" />

    <textarea class="playable playable-js" style="height: 220px;">

    </textarea>

      <input id="reset" type="button" value="Reset" />


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Bouncing balls</title>
    <link rel="stylesheet" href="style.css">

bouncing balls

Ball count:

    <canvas></canvas>


text:
html, body {
  margin: 0;
}

html {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  height: 100%;
}

body {
  overflow: hidden;
  height: inherit;
}

h1 {
  font-size: 2rem;
  letter-spacing: -1px;
  position: absolute;
  margin: 0;
  top: -4px;
  right: 5px;

  color: transparent;
  text-shadow: 0 0 4px white;
}

p {
  position: absolute;
  margin: 0;
  top: 35px;
  right: 5px;
  color: #aaa;
}


text:
// Thanks to Renan Martineli for this version of the demo

// setup canvas

const para = document.querySelector('p');
let count = 0;

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;

// function to generate random number

function random(min,max) {
  const num = Math.floor(Math.random()*(max-min)) + min;
  return num;
};

// function to generate random RGB color value

function randomRGB() {
  return `rgb(${random(0, 255)},${random(0, 255)},${random(0, 255)})`;
}

class Shape {

  constructor(x, y, velX, velY) {
    this.x = x;
    this.y = y;
    this.velX = velX;
    this.velY = velY;
  }

}

class Ball extends Shape {

  constructor(x, y, velX, velY, color, size) {
    super(x, y, velX, velY);

    this.color = color;
    this.size = size;
    this.exists = true;
  }

  draw() {
    ctx.beginPath();
    ctx.fillStyle = this.color;
    ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
    ctx.fill();
  }

  update() {
    if ((this.x + this.size) >= width) {
      this.velX = -(this.velX);
    }

    if ((this.x - this.size) <= 0) {
      this.velX = -(this.velX);
    }

    if ((this.y + this.size) >= height) {
      this.velY = -(this.velY);
    }

    if ((this.y - this.size) <= 0) {
      this.velY = -(this.velY);
    }

    this.x += this.velX;
    this.y += this.velY;
  }

  collisionDetect() {
     for (const ball of balls) {
        if (!(this === ball) && ball.exists) {
           const dx = this.x - ball.x;
           const dy = this.y - ball.y;
           const distance = Math.sqrt(dx * dx + dy * dy);

           if (distance < this.size + ball.size) {
             ball.color = this.color = randomRGB();
           }
        }
     }
  }

}

class EvilCircle extends Shape {

  constructor(x, y) {
    super(x, y, 20, 20);

    this.color = "white";
    this.size = 10;

    window.addEventListener('keydown', (e) => {
      switch(e.key) {
        case 'a':
          this.x -= this.velX;
          break;
        case 'd':
          this.x += this.velX;
          break;
        case 'w':
          this.y -= this.velY;
          break;
        case 's':
          this.y += this.velY;
          break;
      }
    });
  }

  draw() {
    ctx.beginPath();
    ctx.strokeStyle = this.color;
    ctx.lineWidth = 3;
    ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
    ctx.stroke();
  }

  checkBounds() {
    if ((this.x + this.size) >= width) {
      this.x -= this.size;
    }

    if ((this.x - this.size) <= 0) {
      this.x += this.size;
    }

    if ((this.y + this.size) >= height) {
      this.y -= this.size;
    }

    if ((this.y - this.size) <= 0) {
      this.y += this.size;
    }
  }

  collisionDetect() {
    for (const ball of balls) {
      if (ball.exists) {
        const dx = this.x - ball.x;
        const dy = this.y - ball.y;
        const distance = Math.sqrt(dx * dx + dy * dy);

        if (distance < this.size + ball.size) {
          ball.exists = false;
          count--;
          para.textContent = 'Ball count: ' + count;
        }
      }
    }
  }

}

// define array to store balls and populate it

const balls = [];

while (balls.length < 25) {
  const size = random(10, 20);
  const ball = new Ball(
    // ball position always drawn at least one ball width
    // away from the edge of the canvas, to avoid drawing errors
    random(0 + size, width - size),
    random(0 + size, height - size),
    random(-7, 7),
    random(-7, 7),
    randomRGB(),
    size
  );
  balls.push(ball);
  count++;
  para.textContent = 'Ball count: ' + count;
}

const evilBall = new EvilCircle(random(0, width), random(0, height));

function loop() {
  ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
  ctx.fillRect(0, 0, width, height);

  for (const ball of balls) {
    if (ball.exists) {
      ball.draw();
      ball.update();
      ball.collisionDetect();
    }
  }

  evilBall.draw();
  evilBall.checkBounds();
  evilBall.collisionDetect();

  requestAnimationFrame(loop);
}

loop();


text:
# Marking guide for "Adding features to our bouncing balls demo"

The following guide outlines a marking guide for the "Introducing JavaScript Objects" module assessment, part of the MDN Learning Area JavaScript Topic — see [Adding features to our bouncing balls demo](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features). Each subtask detailed in the assessment is listed below, along with an explanation of how many marks the task is worth, and the mark breakdown.

Note: These are guidelines, not set in stone rules — you are of course free to use your judgement on mark awarding when you meet an edge case, or something that isn't clear cut.

The overall mark awarded is out of 44. Work out their final mark, and then divide by 44 and multiply by 100 to give a percentage mark. For reference, you can find a [finished program](main.js) that would be awarded top marks.

## Shape class

* **The `Shape` class definition**: One mark for this.
* **The `Shape` constructor**: Three marks for this. You need to copy the original `Ball()` constructor and remove the `color` and `size` definitions.

## Ball class

* **The `Ball` class definition**: Two marks for deriving from `Shape` using `extends`.

* **The `Ball` constructor**: Six marks for this in total:
  * Two marks for using `super(x, y, velX, velY);` to initialize the `Shape` properties.
  * Two marks for initializing `color` and `size` properties, which should be the same as the ones in the original `Ball` constructor.
  * Two marks for initializing `exists` to `true` in the constructor.

## EvilCircle class

* **The `EvilCircle` class definition**: Two marks for deriving from `Shape` using `extends`.

* **The `EvilCircle` constructor**: Six marks for this in total:
  * Two marks for using `super(x, y, velX, velY);` to initialize the `Shape` properties.
  * Two marks for initializing `color` and `size` properties.
  * Two marks for including the `keydown` event listener code.

* **`draw()`**: Three marks for this.

* **`checkBounds()`**: Five marks for this.

* **`collisionDetect()`** Two marks for this.

## Bringing the evil circle into the program

* **Creating an EvilCircle instance**: Two marks for this. You need to create a new instance of the `EvilCircle`. This must be done in a way that it only runs once, not with every loop iteration. The easiest way to do this is to just include the lines outside the loop function definition, before `loop()` is called.

* **Update the `loop()` code so that the ball functions are only called inside the loop if the current ball exists**: Two marks for this.

* **Call the evil ball instance's `draw()`, `checkBounds()`, and `collisionDetect()` methods**: Two marks for this.

## Implementing the score counter

* **Adding a `<p>` element**: One mark.
* **Adding the provided CSS rule to the bottom of the CSS file**: One mark — the CSS rule needs to be copied exactly as shown in to the bottom of the CSS file.
* **JavaScript updates**: Four marks in total. You need to complete the following tasks:
  * Create a variable that stores a reference to the paragraph — something like this is fine.
  * Keep a count of the number of balls on screen in some way.
  * Increment the count and display the updated number of balls present each time a ball is added to the scene.
  * Decrement the count and display the updated number of balls present each time the evil circle eats a ball (causes it not to exist).


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Simple JSON example</title>
  </head>

  <body>

    <script>
    const superHeroes = {
      "squadName" : "Super hero squad",
      "homeTown" : "Metro City",
      "formed" : 2016,
      "secretBase" : "Super tower",
      "active" : true,
      "members" : [
        {
          "name" : "Molecule Man",
          "age" : 29,
          "secretIdentity" : "Dan Jukes",
          "powers" : [
            "Radiation resistance",
            "Turning tiny",
            "Radiation blast"
          ]
        },
        {
          "name" : "Madame Uppercut",
          "age" : 39,
          "secretIdentity" : "Jane Wilson",
          "powers" : [
            "Million tonne punch",
            "Damage resistance",
            "Superhuman reflexes"
          ]
        },
        {
          "name" : "Eternal Flame",
          "age" : 1000000,
          "secretIdentity" : "Unknown",
          "powers" : [
            "Immortality",
            "Heat Immunity",
            "Inferno",
            "Teleportation",
            "Interdimensional travel"
          ]
        }
      ]
    }
    </script>
  </body>
</html>


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">

    <title>Our superheroes</title>

    <link href="https://fonts.googleapis.com/css?family=Faster+One" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
  </head>

  <body>

      <header>

      </header>

      <section>

      </section>

      <script>

      </script>
  </body>
</html>


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">

    <title>Our superheroes</title>

    <link href="https://fonts.googleapis.com/css?family=Faster+One" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
  </head>

  <body>

      <header>

      </header>

      <section>

      </section>

    <script>

    async function populate() {

      const requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
      const request = new Request(requestURL);

      const response = await fetch(request);
      const superHeroes = await response.json();

      populateHeader(superHeroes);
      populateHeroes(superHeroes);

    }

    function populateHeader(obj) {
      const header = document.querySelector('header');
      const myH1 = document.createElement('h1');
      myH1.textContent = obj.squadName;
      header.appendChild(myH1);

      const myPara = document.createElement('p');
      myPara.textContent = `Hometown: ${obj.homeTown} // Formed: ${obj.formed}`;
      header.appendChild(myPara);
    }

    function populateHeroes(obj) {
      const section = document.querySelector('section');
      const heroes = obj.members;

      for (const hero of heroes) {
        const myArticle = document.createElement('article');
        const myH2 = document.createElement('h2');
        const myPara1 = document.createElement('p');
        const myPara2 = document.createElement('p');
        const myPara3 = document.createElement('p');
        const myList = document.createElement('ul');

        myH2.textContent = hero.name;
        myPara1.textContent = `Secret identity: ${hero.secretIdentity}`;
        myPara2.textContent = `Age: ${hero.age}`;
        myPara3.textContent = 'Superpowers:';

        const superPowers = hero.powers;
        for (const power of superPowers) {
          const listItem = document.createElement('li');
          listItem.textContent = power;
          myList.appendChild(listItem);
        }

        myArticle.appendChild(myH2);
        myArticle.appendChild(myPara1);
        myArticle.appendChild(myPara2);
        myArticle.appendChild(myPara3);
        myArticle.appendChild(myList);

        section.appendChild(myArticle);
      }
    }

    populate();

    </script>
  </body>
</html>


text:
/* || general styles */

html {
  font-family: 'helvetica neue', helvetica, arial, sans-serif;
}

body {
  width: 800px;
  margin: 0 auto;
}

h1, h2 {
  font-family: 'Faster One', cursive;
}

/* header styles */

h1 {
  font-size: 4rem;
  text-align: center;
}

header p {
  font-size: 1.3rem;
  font-weight: bold;
  text-align: center;
}

/* section styles */

section article {
  width: 33%;
  float: left;
}

section p {
  margin: 5px 0;
}

section ul {
  margin-top: 0;
}

h2 {
  font-size: 2.5rem;
  letter-spacing: -5px;
  margin-bottom: 10px;
}


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">

    <title>Our superheroes</title>

    <link href="https://fonts.googleapis.com/css?family=Faster+One" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
  </head>

  <body>

      <header>

      </header>

      <section>

      </section>

    <script>

    async function populate() {

      const requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
      const request = new Request(requestURL);

      const response = await fetch(request);
      const superHeroesText = await response.text();

      const superHeroes = JSON.parse(superHeroesText);
      populateHeader(superHeroes);
      populateHeroes(superHeroes);

    }

    function populateHeader(obj) {
      const header = document.querySelector('header');
      const myH1 = document.createElement('h1');
      myH1.textContent = obj.squadName;
      header.appendChild(myH1);

      const myPara = document.createElement('p');
      myPara.textContent = `Hometown: ${obj.homeTown} // Formed: ${obj.formed}`;
      header.appendChild(myPara);
    }

    function populateHeroes(obj) {
      const section = document.querySelector('section');
      const heroes = obj.members;

      for (const hero of heroes) {
        const myArticle = document.createElement('article');
        const myH2 = document.createElement('h2');
        const myPara1 = document.createElement('p');
        const myPara2 = document.createElement('p');
        const myPara3 = document.createElement('p');
        const myList = document.createElement('ul');

        myH2.textContent = hero.name;
        myPara1.textContent = `Secret identity: ${hero.secretIdentity}`;
        myPara2.textContent = `Age: ${hero.age}`;
        myPara3.textContent = 'Superpowers:';

        const superPowers = hero.powers;
        for (const power of superPowers) {
          const listItem = document.createElement('li');
          listItem.textContent = power;
          myList.appendChild(listItem);
        }

        myArticle.appendChild(myH2);
        myArticle.appendChild(myPara1);
        myArticle.appendChild(myPara2);
        myArticle.appendChild(myPara3);
        myArticle.appendChild(myList);

        section.appendChild(myArticle);
      }
    }

    populate();

    </script>
  </body>
</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Object-oriented JavaScript example</title>

This example requires you to enter commands in your browser's JavaScript console (see What are browser developer tools for more information).


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Object-oriented JavaScript class example</title>

This example requires you to enter commands in your browser's JavaScript console (see What are browser developer tools for more information).


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Object-oriented JavaScript class further exercises</title>

This example requires you to enter commands in your browser's JavaScript console (see What are browser developer tools for more information).


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Bouncing balls</title>
    <link rel="stylesheet" href="style.css">

bouncing balls

    <canvas></canvas>


text:
// set up canvas

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;

// function to generate random number

function random(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

// function to generate random RGB color value

function randomRGB() {
  return `rgb(${random(0, 255)},${random(0, 255)},${random(0, 255)})`;
}

class Ball {

   constructor(x, y, velX, velY, color, size) {
      this.x = x;
      this.y = y;
      this.velX = velX;
      this.velY = velY;
      this.color = color;
      this.size = size;
   }

   draw() {
      ctx.beginPath();
      ctx.fillStyle = this.color;
      ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
      ctx.fill();
   }

   update() {
      if ((this.x + this.size) >= width) {
         this.velX = -(Math.abs(this.velX));
      }

      if ((this.x - this.size) <= 0) {
         this.velX = Math.abs(this.velX);
      }

      if ((this.y + this.size) >= height) {
         this.velY = -(Math.abs(this.velY));
      }

      if ((this.y - this.size) <= 0) {
         this.velY = Math.abs(this.velY);
      }

      this.x += this.velX;
      this.y += this.velY;
   }

   collisionDetect() {
      for (const ball of balls) {
         if (!(this === ball)) {
            const dx = this.x - ball.x;
            const dy = this.y - ball.y;
            const distance = Math.sqrt(dx * dx + dy * dy);

            if (distance < this.size + ball.size) {
              ball.color = this.color = randomRGB();
            }
         }
      }
   }

}

const balls = [];

while (balls.length < 25) {
   const size = random(10,20);
   const ball = new Ball(
      // ball position always drawn at least one ball width
      // away from the edge of the canvas, to avoid drawing errors
      random(0 + size,width - size),
      random(0 + size,height - size),
      random(-7,7),
      random(-7,7),
      randomRGB(),
      size
   );

  balls.push(ball);
}

function loop() {
   ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
   ctx.fillRect(0, 0,  width, height);

   for (const ball of balls) {
     ball.draw();
     ball.update();
     ball.collisionDetect();
   }

   requestAnimationFrame(loop);
}

loop();


text:
html, body {
  margin: 0;
}

html {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  height: 100%;
}

body {
  overflow: hidden;
  height: inherit;
}

h1 {
  font-size: 2rem;
  letter-spacing: -1px;
  position: absolute;
  margin: 0;
  top: -4px;
  right: 5px;

  color: transparent;
  text-shadow: 0 0 4px white;
}


text:
// setup canvas

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;

// function to generate random number

function random(min, max) {
  const num = Math.floor(Math.random() * (max - min + 1)) + min;
  return num;
}

// function to generate random color

function randomRGB() {
  return `rgb(${random(0, 255)},${random(0, 255)},${random(0, 255)})`;
}


text:
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
  advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at sfischer13@ymail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org


text:
# Contribution Guidelines

Please note that this project is released with a [Contributor Code of Conduct](code-of-conduct.md). By participating in this project you agree to abide by its terms.

### Submission Guidelines

Please ensure your pull request adheres to the following guidelines:

- Search previous suggestions before making a new one, as yours may be a duplicate.
- Make an individual pull request for each suggestion.
- Use the following format: `[name](link) - Description.`
- Link additions should be added to the bottom of the relevant section.
- New categories or improvements to the existing categorization are welcome.

### Adding something to this list

If you have something awesome to contribute to this list, this is how you do it.

You'll need a [GitHub account](https://github.com/join)!

1. Access this list's GitHub page: https://github.com/sfischer13/awesome-guitar
2. Click on the `README.md` file.
3. Now click on the edit icon.
4. You can start editing the text of the file in the in-browser editor. Make sure you follow guidelines above. You can use [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/).
5. Say why you're proposing the changes, and then click on "Propose file change".
6. Submit the [pull request](https://help.github.com/articles/using-pull-requests/)!


text:
<!--lint disable double-link-->

# Awesome Guitar [![Awesome](https://awesome.re/badge.svg)](https://awesome.re)

> Useful resources for (beginner) guitar :guitar: players

You might also like [awesome-sheet-music](https://github.com/adius/awesome-sheet-music) and [awesome-music](https://github.com/ciconia/awesome-music).

*Please read the [contribution guidelines](contributing.md) before contributing.*

## Contents

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Community](#community)
- [Learning](#learning)
  - [Background](#background)
  - [Guitar Types](#guitar-types)
  - [Courses](#courses)
  - [Practice](#practice)
  - [Theory](#theory)
- [Playing](#playing)
  - [Songs](#songs)
  - [Equipment](#equipment)
- [Tools](#tools)
  - [Mobile](#mobile)
  - [Websites](#websites)
- [Building](#building)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Community

- [/r/Guitar](https://www.reddit.com/r/Guitar/) - General subreddit about guitars.
- [Music: Practice & Theory](https://music.stackexchange.com/) - Stack Exchange for music.
- [UG](https://www.ultimate-guitar.com/) - Guitar tabs and more.

## Learning

### Background

- [Guitar](https://en.wikipedia.org/wiki/Guitar) - General Wikipedia entry about guitars.

### Guitar Types

- [Acoustic Guitar](https://en.wikipedia.org/wiki/Acoustic_guitar) - Wikipedia entry about acoustic guitars.
- [Electric Guitar](https://en.wikipedia.org/wiki/Electric_guitar) - Wikipedia entry about electric guitars.
- [Guitalele](https://en.wikipedia.org/wiki/Guitalele) - Wikipedia entry about guitaleles.

### Courses

- [Wikibooks](https://en.wikibooks.org/wiki/Guitar) - Free introductory book.
- [Introduction to Guitar](https://www.coursera.org/learn/guitar) - Coursera course.
- [Justin Guitar](https://www.justinguitar.com/) - Free online lessons and great books.

### Practice

- [/r/guitarlessons](https://www.reddit.com/r/guitarlessons/) - Subbreddit for questions about learning to play guitar.

### Theory

- [Open Music Theory](http://openmusictheory.com/) - Open-source, interactive, online textbook.
- [teoría](http://teoria.com/) - Tutorials, exercises, reference and articles about music theory.
- [/r/musictheory](https://www.reddit.com/r/musictheory/) - Subreddit about music theory.

## Playing

### Songs

- [Recommended Guitar Songs](https://github.com/axs221/recommended-guitar-songs) - Lists of songs for beginning, intermediate, advanced and expert players.
- [/r/weeklyguitarist](https://www.reddit.com/r/weeklyguitarist/) - Learn a new song every week!

### Equipment

- [Effects unit](https://en.wikipedia.org/wiki/Effects_unit) - Wikipedia article about effect pedals.
- [/r/guitarpedals](https://www.reddit.com/r/guitarpedals/) - Subreddit for everything related to guitar pedals.

## Tools

### Mobile

- [SciTuner](https://github.com/kreshikhin/SciTuner) - Guitar tuner for iOS.
- [Android-Guitar-Tuner](https://github.com/chRyNaN/Android-Guitar-Tuner) - Guitar tuner for Android.
- [Drum On!](https://f-droid.org/de/packages/se.tube42.drum.android/) - Drum app for Android.
- [Earmouse](https://f-droid.org/de/packages/pk.contender.earmouse/) - Learn intervals and chords on Android.
- [Cythara](https://f-droid.org/de/packages/com.github.cythara/) - Instrument tuner for Android.

### Websites

- [Guitar Dashboard](http://guitardashboard.com/) - Interactive fretboard and circle of fifths.
- [guitar-tuner](https://github.com/googlearchive/guitar-tuner) - Web-based guitar tuner.
- [arpeggio](https://github.com/korywakefield/arpeggio) - Chord naming app.
- [guitar](https://github.com/1j01/guitar) - Interactive fretboard and tablature editor.
- [Guitar Bro](https://github.com/makaroni4/guitar_bro) - Browser game for learning notes on the guitar.
- [GuitarStack](https://github.com/lucaong/guitarstack) - Browser-based effect stack for electric guitars.
- [Pedalboard](https://github.com/DeerMichel/pedalboard) - Online pedalboard for audio manipulation.
- [Fretboard for Guitar and Ukulele](https://guitarstreams.com/tool/fretboard/) - Interactive fretboard for guitar and ukulele.

## Building

- [guitarspecs](https://github.com/gitfrage/guitarspecs) - Build your own electric guitar.
- [/r/Luthier](https://www.reddit.com/r/Luthier/) - Subreddit for luthiers.


text:
---
name: "CI: awesome-lint"
on:
  push:
    branches: [main]
  schedule:
    # 00:00 on Saturday
    - cron: "0 0 * * 6"
jobs:
  awesome-lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - run: npx awesome-lint


text:
---
name: "CI: markdown-link-check"
on:
  push:
    branches: [main]
  schedule:
    # 00:00 on Saturday
    - cron: "0 0 * * 6"
jobs:
  markdown-link-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - uses: gaurav-nelson/github-action-markdown-link-check@v1


text:
#Authors

* Kiran Gangadharan
* Simon Willison
* Stefane Fermigier
* Vivek Agarwal
* Hans Petur Jonsson
* Audrey Roy
* Karan Goel
* Aaron Hill
* Augustus D'Souza
* Philippe Ndiaye
* Lukas Šalkauskas
* Natan L
* David J. Felix
* M. Yasoob
* Adnan Khan (Mahan Marwat)
* Konark Modi
* Mohammad Faizal
* Mohammad Adil
* Sateesh Kumar
* Pankaj Parashar
* Johan Liu
* Vatsala Dorairajan
* Ce Feng
* Samidh
* Manuel Gutierrez
* Victor Felder
* Muhammad Yasoob Ullah Khalid
* Manuel E. Gutierrez
* Jeroen van Wensen
* Frank V
* Hariharan Rangasamy
* Santosh Kumar
* David Sarma
* Dhiraj Thakur
* Yang Yang
* Mushfiq-E Mahabub
* Jalem Raj Rohit
* Erica Melzer
* Y.J. Chen
* Patrick Mazulo
* Harshul Jain
* Michael Martinez
* José Carlos García
* Abishek Bhat
* Karlijn Willems
* Vitaly Pavlenko
* Thawsitt Naing
* Shey Crompton
* Cyril Bois
* ShadowClaw11


text:
## Contributing
* Add sections (optional)
    * Add section title to the contents, on the top
* Add your links (make sure to add a link title)
* Add your name to the authors list
* Send a Pull Request


text:
# Contents

* [Beginner's Delight](#beginners-delight)
* [Resources for Women](#resources-for-women)
* [Why Python?](#why-python)
* [Style Guide and Idioms](#style-guide-and-idioms)
* [Dictionary](#dictionary)
* [Decorators](#decorators)
* [Generators](#generators)
* [Coroutines](#coroutines)
* [Iterators](#iterators)
* [Yield](#yield)
* [Context Managers](#context-managers)
* [Unicode](#unicode)
* [Networking](#networking)
* [Metaclasses](#metaclasses)
* [Documentation](#documentation)
    * [Sphinx](#sphinx)
* [Debugging](#debugging)
    * [Logging](#logging)
* [Testing](#testing)
* [Environments and Environment Management](#environments-and-environment-management)
* [Profiling](#profiling)
* [Packaging](#packaging)
* [Deployment](#deployment)
    * [Fabric](#fabric)
    * [Ansible](#ansible)
* [Contributing to Python Development](#contributing-to-python-development)
* [Warts and Gotchas](#warts-and-gotchas)
* [Web](#web)
    * [Frameworks](#frameworks)
        * [Flask](#flask)
        * [Web2Py](#web2py)
        * [Django](#django)
        * [Bottle](#bottle)
        * [Tornado](#tornado)
        * [Pyramid](#pyramid)
    * [Web Servers](#web-servers)
    * [API and Web Services](#api-and-web-services)
    * [Scraping](#scraping)
* [Mobile Development](#mobile-development)
    * [Kivy](#kivy)
* [Google Glass](#google-glass)
    * [Resources](#resources)
    * [Libraries](#libraries)
* [GUI Programming](#gui-programming)
* [Android](#Android)
    * [SL4A](#sl4a)
* [WSGI](#wsgi)
* [Databases](#databases)
    * [SQLAlchemy](#sqlalchemy)
* [Data Mining](#data-mining)
* [Data Analysis](#data-analysis)
    * [Pandas](#pandas)
* [Data Visualization](#data-visualization)
* [Deep Learning](#deep-learning)
* [IPython Notebooks](#ipython-notebooks)
* [Design Patterns](#design-patterns)
    * [Concurrency Patterns](#concurrency-patterns)
* [Concurrency and Distributed Systems](#concurrency-and-distributed-systems)
* [Functional Programming](#functional-programming)
* [Python 2 vs Python 3](#python-2-vs-3)
    * [Porting to Python 3](#porting-to-python-3)
* [Books](#books)
    * [Free](#free)
    * [Paid](#paid)
* [Online Courses and Challenges](#online-courses-and-challenges)
* [Discussions](#discussions)
* [Conferences and Events](#conferences-and-events)
* [Videos](#videos)
* [Editors and IDE's for Python programming](#editors-and-ides-for-python-programming)
* [Big Data](#bigdata)
* [Curated Python resources from other websites](#curated-python-resources-from-other-websites)
* [Newsletters](#newsletters)
* [Podcasts](#podcasts)
* [Miscellaneous](#miscellaneous)

## Beginner's Delight
* [Beginner's guide to Python](http://wiki.python.org/moin/BeginnersGuide)
* [The Hitchhiker's guide to Python](http://docs.python-guide.org/en/latest/)
* [Learn Python the hard way](http://learnpythonthehardway.org/book/)
* [Learn Python](http://www.learnpython.org/)
* [Google's Python class](https://developers.google.com/edu/python/)
* [Python tracks at Codecademy](http://www.codecademy.com/tracks/python)
* [Python for Beginners](http://www.pythonforbeginners.com/)
* [Intro to Python For Data Science](https://www.datacamp.com/courses/intro-to-python-for-data-science)
* [Best way to learn python](http://net.tutsplus.com/tutorials/the-best-way-to-learn-python/)
* [Python Tips, tools and resources](http://web.archive.org/web/20141010212202/http://lurnq.com/lesson/Getting-started-with-Python-Tips-Tools-and-Resources)
* [Python Koans: Learn Python through TDD](https://github.com/gregmalcolm/python_koans)
* [Beginner projects for a python newbie](http://newcoder.io/)
* [Learn X in Y minutes - Python](http://learnxinyminutes.com/docs/python/)
* [Python Practice book](http://anandology.com/python-practice-book/)
* [Python tools and software](http://www.linux.org/threads/python-tools-and-software.6372/)
* [Facts and myths about Python names and values](http://nedbatchelder.com/text/names.html)
* [*args and **kwargs in python explained](http://freepythontips.wordpress.com/2013/08/04/args-and-kwargs-in-python-explained/)
* [In Python, it’s all about the attributes](http://blog.lerner.co.il/python-attributes/)
* [Common newbie mistakes in Python- Part 1](http://blog.amir.rachum.com/post/54770419679/python-common-newbie-mistakes-part-1)
* [Common newbie mistakes in Python- Part 2](http://blog.amir.rachum.com/post/55024295793/python-common-newbie-mistakes-part-2)
* [PySchools](http://www.pyschools.com/)
* [Some cool Python Tricks](http://www.quora.com/Python-programming-language-1/What-are-some-cool-Python-tricks)
* [if x and if x is not None](http://blog.amir.rachum.com/post/30176371115/you-cant-handle-the-truth)
* [Declaring dependencies in Python](http://blog.ziade.org/2013/04/13/declaring-dependencies-in-python/)
* [Storing and Loading Data with JSON - Serialization/deserialization](http://freepythontips.wordpress.com/2013/08/08/storing-and-loading-data-with-json/)
* [A Guide to Python's Magic Methods](http://www.rafekettler.com/magicmethods.html)
* [Efficient String Concatenation in Python](http://www.skymind.com/~ocrow/python_string/)
* [Be Pythonic: `__init__.py`](http://mikegrouchy.com/blog/2012/05/be-pythonic-__init__py.html)
* [Building an open-source Python application the right way](http://kirang.in/2013/09/09/building-an-open-source-python-application-the-right-way/)
* [Python progression path - From apprentice to guru](http://stackoverflow.com/questions/2573135/python-progression-path-from-apprentice-to-guru)
* [Tutorialspoint python tutorial](http://www.tutorialspoint.com/python/)
* [Python Importing](http://blog.amir.rachum.com/post/63666832095/python-importing)
* [Python's super() considered super!](http://rhettinger.wordpress.com/2011/05/26/super-considered-super/)
* [No Naked Excepts](http://www.wilfred.me.uk/blog/2013/11/03/no-naked-excepts/)
* [Supercharge Your Python Developers](http://www.jeffknupp.com/blog/2013/11/15/supercharge-your-python-developers/)
* [Anti-Patterns in Python Programming](http://lignos.org/py_antipatterns/)
* [The Python IAQ: Infrequently Answered Questions](http://norvig.com/python-iaq.html)
* [Python's objects and classes — a visual guide](http://blog.lerner.co.il/pythons-objects-and-classes-a-visual-guide/)
* [Visualize your Python code being executed](http://www.pythontutor.com/visualize.html)
* [Snakify - a tutorial with exercises and a visual debugger](https://snakify.org/)
* [Learn programming with curated Python projects - DevProjects](https://www.codementor.io/projects/python)
## Resources for Women
* [Pyladies](http://www.pyladies.com/)
* [Diversity in Python Community](https://www.python.org/community/diversity/)
* [Django Girls](http://djangogirls.org/)
* [Pystar](http://pystar.org/)
* [Geek Feminism Python Posts](http://geekfeminism.org/tag/python/)
* [Tarah Wheeler Van Vlack Videos](https://www.youtube.com/user/tarahwheelervanvlack)

## Why Python?
* [Python Success Stories](http://www.python.org/about/success/esr/)
* [Why Python - Slideshare](http://www.slideshare.net/arnav/python-presentation)
* [5 Reasons Why You Should Learn Python Programming](http://www.codercaste.com/2009/10/20/5-reasons-why-you-should-learn-python-programming/)
* [10 Reasons Python Rocks for Research (And a Few Reasons it Doesn’t)](http://www.stat.washington.edu/~hoytak/blog/whypython.html)
* [Why such popularity with Python?](https://programmers.stackexchange.com/questions/5427/why-such-popularity-with-python)
* [Why was Python's popularity so sudden?](https://programmers.stackexchange.com/questions/27207/why-was-pythons-popularity-so-sudden?rq=1)
* [Why Python?](http://www.reddit.com/r/Python/comments/1mb4y4/why_python/)
* [How Python became the language of choice for data science](http://blog.mikiobraun.de/2013/11/how-python-became-the-language-of-choice-for-data-science.html)

## Style Guide and Idioms
* [PEP 8 - Style Guide for Python Code](http://www.python.org/dev/peps/pep-0008/)
* [PEP 8 — the Style Guide for Python Code](http://pep8.org/)
* [Code Like a Pythonista: Idiomatic Python](http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html)
* [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)
* [Python Idioms and Efficiency](https://www.memonic.com/user/pneff/folder/python/id/1bufp)
* [How not to write Python code](http://eikke.com/how-not-to-write-python-code/)
* [Docstring Conventions](http://www.python.org/dev/peps/pep-0257/)

## Dictionary
* [Python hash, id and dictionary order](http://blog.amir.rachum.com/post/54458435089/python-hash-id-and-dictionary-order)
* [Notes on dictionary implementation](http://bugs.python.org/file6941/dictnotes.txt)
* [Python: The Dictionary Playbook](http://blog.amir.rachum.com/post/39501813266/python-the-dictionary-playbook)

## Decorators
* [Understanding Python Decorators in 12 easy steps](http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/)
* [Common uses of Python decorators - Quora](http://www.quora.com/Python-programming-language-1/What-are-common-uses-of-Python-decorators)
* [Common uses of Python decorators - Stack Overflow](http://stackoverflow.com/questions/489720/what-are-some-common-uses-for-python-decorators)
* [Python Decorators](http://pythonconquerstheuniverse.wordpress.com/2012/04/29/python-decorators/)
* [Decorators I: Introduction to Python Decorators](http://www.artima.com/weblogs/viewpost.jsp?thread=240808)
* [The dark side of decorators](http://apiguy.github.io/blog/2013/06/03/the-dark-side-of-decorators/)
* [Thinking out aloud: Python decorators](http://curiosityhealsthecat.blogspot.in/2013/06/thinking-out-aloud-python-decorators_8528.html)
* [Using Python decorators for registering callbacks](http://curiosityhealsthecat.blogspot.in/2013/07/using-python-decorators-for-registering_8614.html)
* [Meta-matters: Using decorators for better Python programming](http://pydanny-event-notes.readthedocs.org/en/latest/PyconAU2011/decorators.html)
* [How can I make a chain of function decorators in Python?](http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python/1594484#1594484)
* [The Python Decorator Library](https://wiki.python.org/moin/PythonDecoratorLibrary)
* [Improve Your Python: Decorators Explained](http://www.jeffknupp.com/blog/2013/11/29/improve-your-python-decorators-explained/)
* [Painless Decorators](http://hackflow.com/blog/2013/11/03/painless-decorators/)
* [Advanced Uses of Python Decorators](https://www.codementor.io/python/tutorial/advanced-use-python-decorators-class-function)

## Generators
* [Improve Your Python: 'yield' and Generators Explained](http://www.jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/)
* [Generator Tricks for Systems Programmers](http://www.dabeaz.com/generators-uk/)
* [Delegating to a Subgenerator in Python 3](http://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator)
* [Generators Will Free Your Mind](http://pyvideo.org/video/3170/generators-will-free-your-mind-0)

## Coroutines
* [A Curious Course on Coroutines and Concurrency](http://www.dabeaz.com/coroutines/)

## Iterators
* [Understanding Iterators and Iterables](http://www.shutupandship.com/2012/01/understanding-python-iterables-and.html)
* [For Loops and Iterators](https://www.codementor.io/python/tutorial/python-generators-and-iterators)

## Import
* [A Python Import Tutorial for Beginners](https://www.codementor.io/python/tutorial/python-path-virtualenv-import-for-beginners)

## Yield
* [Python yield keyword explained](http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained)

## Context Managers
* [Understanding Python's with statement](http://effbot.org/zone/python-with-statement.htm)
* [The Python "with" Statement by Example](http://preshing.com/20110920/the-python-with-statement-by-example/)

## Unicode
* [Explain Unicode like I'm five years old](http://www.reddit.com/r/Python/comments/1g62eh/explain_it_like_im_five_python_and_unicode/)
* [Unicode Pain](http://nedbatchelder.com/text/unipain.html)
* [Python encodings and Unicode](http://eric.themoritzfamily.com/python-encodings-and-unicode.html)
* [The Updated Guide to Unicode on Python](http://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/)
* [More About Unicode in Python 2 and 3](http://lucumr.pocoo.org/2014/1/5/unicode-in-2-and-3/)
* [Unicode in Python: Common Pitfalls](http://blogs.skicelab.com/maurizio/unicode-common-pitfalls.html)
* [Unicode HOWTO](https://docs.python.org/3/howto/unicode.html)

## Networking
* [An intro to Python socket network programming](http://freepythontips.wordpress.com/2013/08/06/python-socket-network-programming/)

## Metaclasses
* [A primer on python metaclasses](http://jakevdp.github.io/blog/2012/12/01/a-primer-on-python-metaclasses/)
* [What is a metaclass in Python?](http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python)
* [Improve Your Python: Metaclasses and Dynamic Classes With Type](http://www.jeffknupp.com/blog/2013/12/28/improve-your-python-metaclasses-and-dynamic-classes-with-type/)

## Documentation
* [Documentation is King](http://kennethreitz.org/documentation-is-king/)
* [Make your open source project documentation suck less](http://web.archive.org/web/20150813232522/http://kirang89.webfactional.com/2013/05/07/make-your-open-source-project-documentation-suck-less/)
* [Python Documentation Network](http://pydoc.net/)
* [Writing great documentation](http://jacobian.org/writing/great-documentation/)

### Sphinx
* [Quick Sphinx documentation for Python](http://scriptsonscripts.blogspot.in/2012/09/quick-sphinx-documentation-for-python.html)

## Environments and Environment Management
* [Virtualenv - The official documentation](http://www.virtualenv.org/en/latest/)
* [Virtual Environments — The Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/dev/virtualenvs/)
* [Virtualenv Tutorial](http://simononsoftware.com/virtualenv-tutorial/)
* [A Primer on virtualenv](http://iamzed.com/2009/05/07/a-primer-on-virtualenv/)
* [How to use Python virtualenv](http://www.pythonforbeginners.com/basics/how-to-use-python-virtualenv)
* [Making Virtualenv play nice with Git](http://hmarr.com/2010/jan/19/making-virtualenv-play-nice-with-git/)
* [Extension to Virtualenv](https://bitbucket.org/dhellmann/virtualenvwrapper)
* [VirtualEnv Burrito = virtualenv + virtualenvwrapper](https://github.com/brainsik/virtualenv-burrito)
* [Virtualenv Tips](http://ericholscher.com/blog/2010/nov/1/virtualenv-tips/)
* [Anaconda Primer](http://caffeineindustries.com/anaconda-primer.html)

## Exceptions
* [Writing and Using Custom Exceptions](https://www.codementor.io/python/tutorial/how-to-write-python-custom-exceptions)

## Debugging
* [Debugging Tools in Python](http://blog.ionelmc.ro/2013/06/05/python-debugging-tools/)
* [Debugging Python Like a Boss](https://zapier.com/engineering/debugging-python-boss/)

### Logging
* [Sentry - realtime event logging and aggregation platform](https://docs.sentry.io/)

## Testing
* [Nose Introduction](http://pythontesting.net/framework/nose/nose-introduction/)
* [Unittest Introduction](http://pythontesting.net/framework/unittest/unittest-introduction/)
* [How To Use Coverage.py With Unittest](https://github.com/audreyr/how-to/blob/master/python/use_coverage_with_unittest.rst)
* [An Extended Introduction to the nose Unit Testing Framework](http://ivory.idyll.org/articles/nose-intro.html)
* [Test strategies for your Python projects](http://web.archive.org/web/20160604121116/http://blog.flaper87.com/post/522b9e560f06d32542ede77f/)
* [Improve Your Python: Understanding Unit Testing](http://jeffknupp.com/blog/2013/12/09/improve-your-python-understanding-unit-testing/)
* [An Introduction to Mocking in Python](http://www.toptal.com/python/an-introduction-to-mocking-in-python)
* [Python Mock Library Comparison](https://garybernhardt.github.io/python-mock-comparison/)

## Profiling
* [A guide to analyzing Python performance](http://www.huyng.com/posts/python-performance-analysis/)
* [Profiling Python Like a Boss](https://zapier.com/engineering/profiling-python-boss/)

## Packaging
* [The Hitchhiker’s Guide to Packaging](http://docs.python-guide.org/en/latest/shipping/packaging/)
* [Sharing Your Labor of Love: PyPI Quick And Dirty](http://hynek.me/articles/sharing-your-labor-of-love-pypi-quick-and-dirty/)
* [Open Sourcing a Python Project the Right Way](http://www.jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/)
* [5 tips for packaging your Python projects](http://ziade.org/2011/08/19/5-tips-for-packaging-your-python-projects/)
* [AOSA: Python Packaging](http://www.aosabook.org/en/packaging.html)
* [The Package Dependency Blues](http://blog.miguelgrinberg.com/post/the-package-dependency-blues)
* [5 Simple Rules For Building Great Python Packages](http://axialcorps.com/2013/08/29/5-simple-rules-for-building-great-python-packages)
* [Unofficial Windows Binaries for Python Extension Packages](http://www.lfd.uci.edu/~gohlke/pythonlibs/)
* [Py2app: Standalone Mac OS X applications](http://pythonhosted.org/py2app/)
* [PyInstaller: Stand-alone executables, under Windows, Linux, Mac OS X, Solaris and AIX](http://www.pyinstaller.org/)
* [setup.py tricks](http://www.pydanny.com/python-dot-py-tricks.html)
* [Python Packaging: Quick Tutorial & Checklist](https://holypython.com/python-packaging-quick-tutorial-checklist/)

## Deployment
* [Solid Python Deployments for Everybody](http://hynek.me/talks/python-deployments)

### Fabric
* [Using Fabric in a development environment](http://www.pythonforbeginners.com/systems-programming/how-to-use-fabric-in-a-development-environment/)
* [Capture output from parallel execution with Fabric](http://dmsimard.com/2013/11/29/capture-output-from-parallel-execution-with-fabric/)

### Ansible
* [Example Ansible scripts for building a full AWS stack](https://github.com/rabidgremlin/dev-ops-snippets/tree/master/aws-full-stack)

## Contributing to Python Development
* [The CPython Developer's Guide](https://github.com/python/devguide)

## Warts and Gotchas
* [Python 2.x gotcha's and landmines](http://stackoverflow.com/questions/530530/python-2-x-gotchas-and-landmines)
* [Python Warts](https://wiki.python.org/moin/PythonWarts)
* [Python Gotchas](http://blog.artlogic.com/2013/04/12/python-gotchas/)
* [Common Pitfalls in Python](http://stackoverflow.com/questions/1011431/common-pitfalls-in-python/1321075#1321075)

## Web
* [Python and Realtime Web](http://mrjoes.github.io/2013/06/21/python-realtime.html)
* [A Python guide to handling HTTP request failures](https://www.mobify.com/blog/http-requests-are-hard/)

### Frameworks
* [Django vs Flask vs Pyramid: Choosing a Python Web Framework](https://www.airpair.com/python/posts/django-flask-pyramid)

#### Flask
* [Flask Mega Tutorial Series](http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world)
* [Designing a RESTful API with Python and Flask](http://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask)
* [How I structure my Flask apps](http://web.archive.org/web/20160324134243/http://mattupstate.com/python/2013/06/26/how-i-structure-my-flask-applications.html)
* [Designing a RESTful API using Flask-RESTful](http://blog.miguelgrinberg.com/post/designing-a-restful-api-using-flask-restful)
* [Rapid Web Prototyping with Lightweight Tools](http://www.pixelmonkey.org/2013/03/13/rapid-web-prototyping-with-lightweight-tools)
* [Build a Simple, Static, Markdown-Powered Blog with Flask](http://www.jamesharding.ca/posts/simple-static-markdown-blog-in-flask/)
* [How to Build an API with Python and Flask](http://web.archive.org/web/20150502090353/http://tech.pro/tutorial/1213/how-to-build-an-api-with-python-and-flask)
* [Create a Simple Music Streaming App with Flask](http://web.archive.org/web/20140424194048/http://pypix.com/python/create-simple-music-streaming-app-flask/)
* [Adding full-text search to the flask note-taking app](http://charlesleifer.com/blog/saturday-morning-hacks-adding-full-text-search-to-the-flask-note-taking-app/)
* [Kickstarting Flask on Ubuntu - Setup and Deployment](https://realpython.com/blog/python/kickstarting-flask-on-ubuntu-setup-and-deployment)
* [OAuth Authentication with Flask](http://blog.miguelgrinberg.com/post/oauth-authentication-with-flask)
* [Building a blog using Flask and AngularJS](http://tutsbucket.com/tutorials/building-a-blog-using-flask-and-angularjs-part-1/)
* [A repository of Flask resources](https://github.com/humiaozuzu/awesome-flask)
* [Building a Movie Recommendation Service with Spark & Flask](https://www.codementor.io/spark/tutorial/building-a-recommender-with-apache-spark-python-example-app-part2)
* [Building a Kanban App with Flask and React](http://blog.tryolabs.com/2015/04/30/building-a-kanban-app-in-react/)

##### Books
* [Explore Flask](https://exploreflask.com)

#### Web2Py
* [Building a Twitter clone in Web2Py](http://fragile.org.uk/2013/06/twitter-clone-tutorial-in-web2py-part-1-getting-started/)

#### Django
* [The official Django documentation](https://docs.djangoproject.com/)
* [Getting started with Django](http://web.archive.org/web/20160618193743/http://gettingstartedwithdjango.com/)
* [Starting a Django 1.6 Project the Right Way](http://www.jeffknupp.com/blog/2013/12/18/starting-a-django-16-project-the-right-way/)
* [Building a hacker news clone in Django](http://arunrocks.com/building-a-hacker-news-clone-in-django-part-1/)
* [Djen of Django](http://agiliq.com/books/djenofdjango/)
* [Deploying Django](http://www.rdegges.com/deploying-django/)
* [Django by Example](http://www.lightbird.net/dbe/)
* [Tango with Django](http://www.tangowithdjango.com/book/)
* [Django Screencasts](https://godjango.com/)
* [Let’s TDD a Simple App in Django](http://web.archive.org/web/20131212220546/http://pypix.com/django/tdd-in-django/)
* [Django runserver and stunnel for testing HTTPS](http://mgile.com/post/4729505823/django-stunnel)
* [Scaling database with Django and HAProxy](http://engineering.hackerearth.com/2013/10/07/scaling-database-with-django-and-haproxy/)
* [Classy Class-Based Views](http://ccbv.co.uk/)
* [Django models, encapsulation and data integrity](http://www.dabapps.com/blog/django-models-and-encapsulation/)
* [11 Things I Wish I Knew About Django Development Before I Started My Company](https://medium.com/cs-math/11-things-i-wish-i-knew-about-django-development-before-i-started-my-company-f29f6080c131)
* [Build a Shopify App in 15 minutes with Django](http://gavinballard.com/shopify-app-in-15-minutes-with-django)
* [A repository of must-watch videos about Django](https://github.com/rosarior/django-must-watch)
* [Django Girls Tutorial](https://tutorial.djangogirls.org/en/)
* [Mastering Django](http://masteringdjango.com/)
* [Building a Wine Review & Recommendation Site with Django](https://www.codementor.io/python/tutorial/build-data-products-django-machine-learning-clustering-user-preferences)

#### Bottle
* [Developing with Bottle- Part 1](http://www.realpython.com/blog/python/developing-with-bottle-part-1/)

#### Tornado
* [Tornado official documentation](http://www.tornadoweb.org/en/stable/)
* [Fastest database for Tornado](http://www.peterbe.com/plog/fastestdb)

#### Pyramid
* [Pyramid official documentation](http://docs.pylonsproject.org/en/latest/docs/pyramid.html)
* [How To Use the Pyramid Framework To Build Your Python Web App on Ubuntu](https://www.digitalocean.com/community/tutorials/how-to-use-the-pyramid-framework-to-build-your-python-web-app-on-ubuntu)
* [Building an AJAX Hello-World with Pyramid](https://www.codementor.io/javascript/tutorial/build-ajax-helloworld-pyramid)

### Web Servers
* [A Comparison of Web Servers for Python Based Web Applications](https://www.digitalocean.com/community/articles/a-comparison-of-web-servers-for-python-based-web-applications)
* [Setting Up a Rock Solid Python Development Web Server](https://fgimian.github.io/blog/2012/12/08/setting-up-a-rock-solid-python-development-web-server/)

### API and Web Services
* [How to access various web services in python](http://www.pythonforbeginners.com/python-on-the-web/how-to-access-various-web-services-in-python/)
* [Glassdoor API](https://github.com/hackerlist/glassdoor)

### Scraping
* [Web Scraping with BeautifulSoup](http://www.pythonforbeginners.com/python-on-the-web/web-scraping-with-beautifulsoup/)
* [Web Scraping 101 with Python](http://www.gregreda.com/2013/03/03/web-scraping-101-with-python/)
* [Web Scraping with Scrapy Framework](http://doc.scrapy.org/en/latest/intro/tutorial.html)
* [How to automatically search and download torrents with Python and Scrapy](http://fuzz-box.blogspot.in/2013/03/how-to-automatically-search-download-torrent-python-scrapy.html)

## Mobile Development
### Kivy
* [Creating an Application in Kivy: Part 1](http://buchuki.com/index.php/2013/06/13/creating-an-application-in-kivy-part-1/)

## Google Glass
### Resources
* [The official Python Quick Start for Google Glass](https://developers.google.com/glass/quickstart/python)

### Libraries
#### glass.py
* [Create python web applications for Google Glass](https://github.com/SamyPesse/glass.py)

## Image Processing
* [Python Image Library - Examples](http://web.archive.org/web/20150814055529/http://www.riisen.dk/dop/pil.html)
* [Digital Image Basics](https://holypython.com/digital-image-basics-with-pil-library-in-python/)
* [How to Watermark Images with Python](https://holypython.com/how-to-watermark-images-w-python-pil/)

## GUI Programming
* [Python.org GUI Overview Page](https://wiki.python.org/moin/GuiProgramming)
* [PyQt5 Tutorial](https://coderslegacy.com/python/pyqt5-tutorial/)
* [Standalone executable for PyQt4 on Windows](http://www.py2exe.org/index.cgi/Py2exeAndPyQt)
* [Kivy GUI for cross-platform applications](http://kivy.org/)
* [EffBot.org Introduction to Tkinter](http://effbot.org/tkinterbook/tkinter-index.htm)
* [Pytkgen: JSON to Tkinter GUI Generator](https://github.com/tmetsch/pytkgen)
* [Intro to programming with Python and Tkinter](https://wiki.python.org/moin/Intro%20to%20programming%20with%20Python%20and%20Tkinter)
* [Tkinter Course - Create Graphic User Interfaces in Python Tutorial](https://www.youtube.com/watch?v=YXPyB4XeYLA)
* [Python - GUI Programming (Tkinter)](https://www.tutorialspoint.com/python/python_gui_programming.htm)
* [Creating GUI Programs Fast & Easy Tutorial](https://holypython.com/creating-gui-programs-w-python-fast-easy-pysimplegui/)

## Game Development
* [Pygame Tutorial](https://coderslegacy.com/python/python-pygame-tutorial/)

## Android
### SL4A
* [Making the Physics Gizmo app for Android using SL4A](http://www.brokenairplane.com/2011/08/im-not-texting-im-programming.html)

## WSGI
* [Getting started with WSGI](http://lucumr.pocoo.org/2007/5/21/getting-started-with-wsgi/)
* [WSGI Basics](http://agiliq.com/blog/2013/07/basics-wsgi/)

## Databases

### SQLAlchemy
* [SQLAlchemy](http://www.aosabook.org/en/sqlalchemy.html)
* [SQLAlchemy and you](http://lucumr.pocoo.org/2011/7/19/sqlachemy-and-you/)
* [Essential SQLAlchemy Tips and Techniques](http://web.archive.org/web/20140424034838/http://pypix.com/tools-and-tips/essential-sqlalchemy/)
* [10 common stumbling blocks for SQLAlchemy newbies](http://alextechrants.blogspot.fr/2013/11/10-common-stumbling-blocks-for.html)
* [An SQLAlchemy Cheatsheet](https://www.codementor.io/python/tutorial/understanding-sqlalchemy-cheat-sheet)

## Data Mining
* [Linux system mining using Python](http://architects.dzone.com/articles/linux-system-mining-python)

## Data Analysis
* [Practical Data Science in Python](http://radimrehurek.com/data_science_python/)
* [Document Clustering with Python](http://brandonrose.org/clustering)
* [Data Science with Python & R: Data Frames](https://www.codementor.io/python/tutorial/python-vs-r-for-data-science-data-frames-i)
* [Data Science with Python & R: Exploratory Data Analysis](https://www.codementor.io/python/tutorial/data-science-python-r-exploratory-data-analysis-visualization)
* [Data Science with Python & R: Sentiment Classification Using Linear Methods](https://www.codementor.io/python/tutorial/data-science-python-r-sentiment-classification-machine-learning)
* [Scientific Python Cheatsheet](https://ipgp.github.io/scientific_python_cheat_sheet/)
* [Python Basics For Data Science](https://www.datacamp.com/community/tutorials/python-data-science-cheat-sheet-basics)
* [SciPy Lecture Notes: One document to learn numerics, science, and data with Python](http://www.scipy-lectures.org/)
* [Machine Learning Algorithms with Python](https://holypython.com/ml)

### Pandas
* [A 10 min intro to Pandas](http://pandas.pydata.org/pandas-docs/stable/10min.html)
* [Pandas and Python: Top 10](http://manishamde.github.io/blog/2013/03/07/pandas-and-python-top-10/)
* [Learn Pandas](https://bitbucket.org/hrojas/learn-pandas)
* [Faster Data Processing in Python](http://nbviewer.ipython.org/github/sanand0/ipython-notebooks/blob/master/Faster%20Data%20Processing%20in%20Python.ipynb)
* [Learn More About Pandas By Building and Using a Weighted Average Function](http://pbpython.com/weighted-average.html)

## Data Visualization
* [How to Create Matplotlib Animations](https://holypython.com/how-to-create-matplotlib-animations-the-ultimate-guide/)
* [How to Save Matplotlib Animations](https://holypython.com/how-to-save-matplotlib-animations-the-ultimate-guide/)
* [How to Create Plotly Animations](https://holypython.com/how-to-create-plotly-animations-the-ultimate-guide/)
* [How to Save Plotly Animations](https://holypython.com/how-to-save-plotly-animations-the-ultimate-guide/)
* [How to Map Charts in Python](https://holypython.com/how-to-create-map-charts-in-python-w-plotly-mapbox/)

## Deep Learning
* [Build your first Neural Network with Python and Keras](http://machinelearningmastery.com/tutorial-first-neural-network-python-keras/)
* [Building powerful image classification models using very little data](https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html)
* [Python implementation of several famous Deep Learning papers](https://github.com/fchollet/keras/tree/master/examples)

## IPython Notebooks
* [A Gallery of Interesting IPython Notebooks](https://github.com/ipython/ipython/wiki/A-gallery-of-interesting-IPython-Notebooks)
* [A collection of not-so-obvious Python stuff you should know!](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/not_so_obvious_python_stuff.ipynb?create=1)
* [Peter Norvig's Notebook Collection](http://norvig.com/ipython/)
* [Statistical Natural Language Processing in Python | How To Do Things With Words. And Counters.](http://nbviewer.ipython.org/url/norvig.com/ipython/How%20to%20Do%20Things%20with%20Words.ipynb)
* [Analysis of World Bank Fertility Rate Data With Statsmodels PCA](http://nbviewer.ipython.org/urls/umich.box.com/shared/static/6m7f4lw9bdog241kqcmb.ipynb)
* [Modeling the Last Flight of MH370 with a Markov Chain Monte Carlo Method](http://nbviewer.ipython.org/github/myhrvold/MH370_MCMC/blob/master/MH370_MC_ConorMyhrvold.ipynb)
* [Differential Equations in Data Science](http://nbviewer.ipython.org/github/URXtech/techblog/blob/master/continuousTimeMarkovChain/markovChain.ipynb)
* [Stegotons: solitary waves arising in non-dispersive periodic media](http://nbviewer.ipython.org/gist/ketch/8554686)
* [The Traveling Salesperson Problem](http://nbviewer.ipython.org/url/norvig.com/ipython/TSP.ipynb)
* [Scientific Computing with Python](http://nbviewer.ipython.org/url/atwallab.cshl.edu/teaching/QBbootcamp3.ipynb)
* [Gaussian Blur](http://nbviewer.ipython.org/urls/raw.github.com/mroberts3000/GpuComputing/master/IPython/GaussianBlur.ipynb)
* [Understanding the FFT Algorithm](http://nbviewer.ipython.org/url/jakevdp.github.io/downloads/notebooks/UnderstandingTheFFT.ipynb)
* [lifelines: Survival Analysis in Python](http://lifelines.readthedocs.io)
* [Economics Simulation](http://nbviewer.ipython.org/url/norvig.com/ipython/Economics.ipynb)
* [An Introduction to Bayesian Methods for Multilevel Modeling](http://nbviewer.ipython.org/urls/raw.github.com/fonnesbeck/multilevel_modeling/master/multilevel_modeling.ipynb)
* [The Floods of the Nile](http://nbviewer.jupyter.org/github/LCAV/SignalsOfTheDay/blob/master/Floods_of_the_Nile/FloodsOfTheNile.ipynb)
* [Animal Foraging and the Evolution of Goal-Directed Cognition](http://nbviewer.ipython.org/urls/raw.github.com/rhiever/Intro-to-Evolutionary-Modeling/master/Animal%2520Foraging%2520and%2520the%2520Evolution%2520of%2520Goal-Directed%2520Cognition.ipynb)
* [Wikipedia data scraping functions](http://nbviewer.ipython.org/urls/raw.github.com/brianckeegan/Wikipedia/master/wikipedia_scraping.ipynb)
* [Python for Geosciences](https://github.com/koldunovn/python_for_geosciences)
* [Media Signal Processing](http://www.mat.ucsb.edu/201A/)
* [Get Started with Python](https://github.com/rajathkumarmp/Python-Lectures)
* [A gallery of data science IPython notebooks](https://github.com/donnemartin/data-science-ipython-notebooks)
* [Jupyter Notebooks derived from Allen Downey's Think Bayes](https://github.com/rlabbe/ThinkBayes)

## Design Patterns
* [Python Best Practice Patterns by Vladimir Keleshev](http://www.youtube.com/watch?v=GZNUfkVIHAY)
* [28 Design Patterns in Python and Several Other Languages](https://github.com/oxnz/design-patterns)
* [Factory Pattern In Python](http://web.archive.org/web/20131008172420/http://dailypython.wordpress.com/2013/08/04/factory-pattern-in-python/)
* [Wishful Coding in Python: a Problem Solving Philosophy](https://www.codementor.io/python/tutorial/wishful-coding-python-solving-big-problems)

### Concurrency Patterns
* [Wasp's Nest: The Read-Copy-Update Pattern In Python](http://emptysqua.re/blog/wasps-nest-read-copy-update-python/)

## Concurrency and Distributed Systems
* [Gevent and ZeroMQ](http://architects.dzone.com/articles/gevent-zeromq)
* [Celery: Distributed Task Queue](https://celery.readthedocs.org/en/latest/getting-started/index.html)
* [Gevent for the working Python developer](http://sdiehl.github.io/gevent-tutorial/)

## Functional Programming
* [Recursion Explained with the Flood Fill Algorithm](http://inventwithpython.com/blog/2011/08/11/recursion-explained-with-the-flood-fill-algorithm-and-zombies-and-cats/)
* [Functional Programming in Python](https://web.archive.org/web/20140105103917/http://pypix.com/python/functional-programming/)

## Python 2 vs 3
* [Python 2 or 3 ?](http://wiki.python.org/moin/Python2orPython3)
* [Python 3 Q & A](http://python-notes.curiousefficiency.org/en/latest/python3/questions_and_answers.html)
* [About Python 3](http://alexgaynor.net/2013/dec/30/about-python-3/)
* [Let’s talk about Python 3.0](http://www.b-list.org/weblog/2008/dec/05/python-3000/)
* [Pros and Cons about Python 3](http://lucumr.pocoo.org/2010/1/7/pros-and-cons-about-python-3/)

### Porting to Python 3
* [Python 3 Wall of Superpowers(Packages)](https://python3wos.appspot.com/)
* [How to support both Python 2 and 3](http://ondrejcertik.blogspot.com/2013/08/how-to-support-both-python-2-and-3.html)
* [How CherryPy supports Python 2 and 3](http://pyvideo.org/video/689/how-cherrypy-supports-python-2-and-3)
* [Porting to Python 3 - The Book Site](http://python3porting.com/)
* [Easy, clean, reliable Python 2/3 compatibility](http://python-future.org/)

## Books
### Free
* [Free Programming Books - Python](https://github.com/vhf/free-programming-books/blob/master/free-programming-books.md#python)
* [Python Programming - Free Computer Books](http://freecomputerbooks.com/langPythonBooks.html)
* [Some free books on Python](http://pythonbooks.revolunet.com/)
* [Invent with Python](http://inventwithpython.com/chapters/)
* [Think Python](http://www.greenteapress.com/thinkpython/thinkpython.pdf)
* [Byte of Python](http://files.swaroopch.com/python/byte_of_python.pdf)
* [Snake Wrangling for Kids](http://www.briggs.net.nz/snake-wrangling-for-kids.html)
* [Data Structures and Algorithms with Object-Oriented Design Patterns in Python](http://www.brpreiss.com/books/opus7/html/book.html)
* [Python programming on wikibooks](http://en.wikibooks.org/wiki/Python_Programming)
* [Dive Into Python 3](http://getpython3.com/diveintopython3/)
* [Programming Computer Vision with Python](http://programmingcomputervision.com/)
* [Python for you and me](http://pymbook.readthedocs.org/en/latest/)
* [Program Arcade Games With Python And Pygame](http://programarcadegames.com/)
* [Interactive python-problem solving](http://interactivepython.org/runestone/static/pythonds/index.html)
* [How to make mistakes in Python](http://www.oreilly.com/programming/free/files/how-to-make-mistakes-in-python.pdf)
* [Think Stats: Exploratory Data Analysis in Python](http://greenteapress.com/thinkstats2/html/index.html)

### Paid
* [Some list of paid books on python](http://wiki.python.org/moin/PythonBooks)
* [Porting to Python 3 - The Book Site](http://python3porting.com/)

## Online Courses and Challenges
* [A Gentle Introduction to Programming Using Python](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-189-a-gentle-introduction-to-programming-using-python-january-iap-2011/index.htm)
* [Python Challenges](http://www.pythonchallenge.com/)
* [Project Euler](http://projecteuler.net)
* [Interactive, test-driven Python coding challenges](https://github.com/donnemartin/interactive-coding-challenges)

## Discussions
* [What's your favorite programming language?](https://news.ycombinator.com/item?id=3746692)
* [What is your primary programming language?](https://news.ycombinator.com/item?id=5944863)
* [StackOverflow - Python beyond the basics](http://stackoverflow.com/questions/92230/python-beyond-the-basics)
* [StackOverflow - Python progression path from apprentice to guru](http://stackoverflow.com/questions/2573135/python-progression-path-from-apprentice-to-guru)
* [StackOverflow - Zen of Python](http://web.archive.org/web/20130801022115/http://stackoverflow.com/questions/228181/zen-of-python)
* [StackOverflow - Hidden Features of Python](http://stackoverflow.com/questions/101268/hidden-features-of-python)

## Conferences and events
* [Official PyCon website](http://www.pycon.org/)
* [Lanyrd - Python Conference and Events](http://lanyrd.com/topics/python/)
* [Meetup.com - Python Meetup Groups](http://python.meetup.com/)

## Videos
* [Guido Van Rossum on the history of Python](http://www.youtube.com/watch?v=ugqu10JV7dk)
* [Type Hints - Guido van Rossum - PyCon 2015](https://www.youtube.com/watch?v=2wDvzy6Hgxg)
* [PyVideo](http://pyvideo.org/)
* [Khan Academy - Introduction to programming and computer science using Python](https://www.khanacademy.org/science/computer-science)
* [What are some "must-watch" Python videos ?](http://www.reddit.com/r/Python/comments/1rs7ub/what_are_some_mustwatch_python_videos/)
* [Lanyrd - Python Videos](http://lanyrd.com/topics/python/video/)
* [Python Videos, Tutorials and Screencasts at ShowMeDo](http://showmedo.com/videotutorials/python)
* [Talks by Brandon Rhodes](http://rhodesmill.org/brandon/talks/)
* [A Billion Rows per Second: Metaprogramming Python for Big Data](https://www.youtube.com/watch?v=rXj5nayS7Yg)
* [The newboston site tutorial](https://thenewboston.com/videos.php?cat=36) - [Python 3 version](https://thenewboston.com/videos.php?cat=98)
* [Python Programming Tutorials](https://www.youtube.com/playlist?list=PLEA1FEF17E1E5C0DA&feature=plcp)
* [Must-watch videos about Python by s16h](https://github.com/s16h/py-must-watch)

## Editors and IDE's for Python programming
* [Setting up Sublime Text for Python Development](http://dbader.org/blog/setting-up-sublime-text-for-python-development)
* [Sublime Text 2 Love by Kenneth Reitz](http://kennethreitz.org/sublime-text-2-love/)
* [Sublime Text 2 tips for Python and web developers](http://opensourcehacker.com/2012/05/11/sublime-text-2-tips-for-python-and-web-developers/)
* [PyCharm](http://www.jetbrains.com/pycharm/)
* [Geany](http://geany.org/)
* [Geany with pylint](http://michaeljaylissner.com/blog/using-pylint-in-geany)
* [PyDev](http://pydev.org)
* [Ninja-IDE](http://ninja-ide.org/)
* [Emacs setup for python development environment](http://web.archive.org/web/20141026080721/http://caisah.info/archive/emacs-for-python.pdf)
* [Emacs for python](https://github.com/gabrielelanaro/emacs-for-python)
* [Python Anywhere](https://www.pythonanywhere.com/)
* [PythonBuddy](http://pythonbuddy.com/)
* [ExtendsClass](https://extendsclass.com/python.html)

## BigData
* [Mind map of Python for Big Data](http://www.xmind.net/m/WvfC/?goback=%2Emid_I2756675707*43_*1#%21)

## Curated Python resources from other websites
* [Python Resources (All) - Codesters.org](http://web.archive.org/web/20160401215551/http://codesters.org/resource/topic/python/all/)
* [Learn Python Online](https://www.codementor.io/learn-python-online)
* [Programming Community Curated Python Tutorials & Courses](https://hackr.io/tutorials/learn-python)
* [Classpert: Find and compare online courses from multiple providers](https://classpert.com/python-programming)

## Newsletters
* [Pycoder's](http://pycoders.com/)
* [Python Weekly](http://www.pythonweekly.com/)

## Podcasts
* [Podcast.\__init__](http://podcastinit.com/)
* [Talk Python To Me](https://talkpython.fm/)

## Miscellaneous
* [Good to great Python reads](http://jessenoller.com/good-to-great-python-reads/)
* [How to Create Barcodes in Your PDFs with Python](http://java.dzone.com/articles/how-create-barcodes-your-pdfs)
* [What are some good Python interview questions ?](http://www.quora.com/What-are-good-Python-interview-questions)
* [Python innards introduction](http://tech.blog.aknin.name/2010/04/02/pythons-innards-introduction/)
* [Projects - A list of practical projects](https://github.com/thekarangoel/Projects)
* [More Python Tutorials](http://web.archive.org/web/20141030172027/http://www.awaretek.com/tutorials.html)
* [Developer Tools in Python](http://pypix.com/tools-and-tips/developer-tools/)
* [The Best of Python in 2013](http://pypix.com/roundups/best-python-2013/)
* [Top Projects of 2013](http://us4.campaign-archive2.com/?u=9735795484d2e4c204da82a29&id=384b699bca&e=1826c8bb55)
* [Advanced Regular Expression Tips and Techniques](http://pypix.com/tools-and-tips/advanced-regular-expression-tips-techniques/)
* [Advanced Data Structures in Python](http://pypix.com/python/advanced-data-structures-python/)
* [Simple Sudoku solver in python](http://freepythontips.wordpress.com/2013/09/01/sudoku-solver-in-python/)
* [Looking Inside Dropbox – Security whitepaper](http://freepythontips.wordpress.com/2013/08/30/looking-inside-dropbox-whitepaper/)
* [Why sorting an array makes a Python loop faster](http://rickystewart.wordpress.com/2013/09/03/why-sorting-an-array-makes-a-python-loop-faster/)
* [Python Integer object implementation](http://www.laurentluce.com/posts/python-integer-objects-implementation/)
* [The history of Python - Blog](http://python-history.blogspot.in/)
* [The ever useful and neat subprocess module](http://sharats.me/the-ever-useful-and-neat-subprocess-module.html)
* [Hidden Features in Python](http://stackoverflow.com/questions/101268/hidden-features-of-python/111176)
* [Python module of the week](http://pymotw.com/2/)
* [Become a programmer](http://programming-motherfucker.com/become.html#Python)
* [Python tutor](http://pythontutor.com/)
* [Best developer blogs about python - Quora](https://www.quora.com/Python-programming-language-1/What-are-the-best-developer-blogs-about-Python-development)
* [Python blogs -Stackoverflow](http://web.archive.org/web/20130516092627/http://stackoverflow.com/questions/152480/python-blogs-that-you-regularly-follow)
* [Abstracting Control Flow](http://hackflow.com/blog/2013/10/08/abstracting-control-flow/)
* [Python Algorithm Visualizations done in Python running in the browser](http://pyalgoviz.appspot.com/)
* [Solving Every Sudoku Puzzle](http://norvig.com/sudoku.html)
* [IHeartPy!](http://www.iheartpy.com/)
* [Difference Between 2 Images: Pixel Level Scanning](https://holypython.com/python-code-to-show-the-difference-between-2-images/)


text:
[package]
name = "extreme_bevy"
version = "0.1.0"
edition = "2021"

[dependencies]
bevy = "0.11"
bevy_ggrs = { version = "0.13", features = ["wasm-bindgen"] }
bevy_matchbox = { version = "0.7", features = ["ggrs"] }
bevy_asset_loader = "0.17"

repo_language: Rust


text:
# Extreme Bevy

Extreme Bevy is what you end up with by following my [tutorial series on how to make a low-latency p2p web game](https://helsing.studio/posts/extreme-bevy).

There game can be played here: https://helsing.studio/extreme

It's a showcase on how to use the following together:

- [Bevy](https://github.com/bevy/bevyengine): ECS game engine for rust users.
- [GGRS](https://github.com/gschup/ggrs) for rollback networking
- [Matchbox](https://github.com/johanhelsing/matchbox) for p2p connections between browsers

## Word of caution

I intend to keep the git history of this repo as clean as possible. That means that whenever there is a new major version of one of my dependencies (or a bug fix). I'll rebase the history, instead of putting the commit at the end. That way I can easily link from the tutorial to relevant commits in the history in this repo. It also means I will force-push main and move tags around.

## License

This project is licensed under [CC0 1.0 Universal](LICENSE). I'd be happy to hear if you found it useful or made anything with it, though! [@johanhelsing@mastodon.social](https://mastodon.social/@johanhelsing).


text:
use bevy::prelude::*;

#[derive(Component)]
pub struct Player {
    pub handle: usize,
}

#[derive(Component, Reflect, Default)]
pub struct BulletReady(pub bool);

#[derive(Component, Reflect, Default)]
pub struct Bullet;

#[derive(Component, Reflect, Default, Clone, Copy)]
pub struct MoveDir(pub Vec2);


text:
use bevy::prelude::*;
use bevy_ggrs::ggrs;

const INPUT_UP: u8 = 1 << 0;
const INPUT_DOWN: u8 = 1 << 1;
const INPUT_LEFT: u8 = 1 << 2;
const INPUT_RIGHT: u8 = 1 << 3;
const INPUT_FIRE: u8 = 1 << 4;

pub fn input(_: In<ggrs::PlayerHandle>, keys: Res<Input<KeyCode>>) -> u8 {
    let mut input = 0u8;

    if keys.any_pressed([KeyCode::Up, KeyCode::W]) {
        input |= INPUT_UP;
    }
    if keys.any_pressed([KeyCode::Down, KeyCode::S]) {
        input |= INPUT_DOWN;
    }
    if keys.any_pressed([KeyCode::Left, KeyCode::A]) {
        input |= INPUT_LEFT
    }
    if keys.any_pressed([KeyCode::Right, KeyCode::D]) {
        input |= INPUT_RIGHT;
    }
    if keys.any_pressed([KeyCode::Space, KeyCode::Return]) {
        input |= INPUT_FIRE;
    }

    input
}

pub fn direction(input: u8) -> Vec2 {
    let mut direction = Vec2::ZERO;
    if input & INPUT_UP != 0 {
        direction.y += 1.;
    }
    if input & INPUT_DOWN != 0 {
        direction.y -= 1.;
    }
    if input & INPUT_RIGHT != 0 {
        direction.x += 1.;
    }
    if input & INPUT_LEFT != 0 {
        direction.x -= 1.;
    }
    direction.normalize_or_zero()
}

pub fn fire(input: u8) -> bool {
    input & INPUT_FIRE != 0
}


text:
use bevy::{math::Vec3Swizzles, prelude::*, render::camera::ScalingMode};
use bevy_asset_loader::prelude::*;
use bevy_ggrs::{ggrs::PlayerType, *};
use bevy_matchbox::prelude::*;
use components::*;
use input::*;

mod components;
mod input;

struct GgrsConfig;

impl ggrs::Config for GgrsConfig {
    // 4-directions + fire fits easily in a single byte
    type Input = u8;
    type State = u8;
    // Matchbox' WebRtcSocket addresses are called `PeerId`s
    type Address = PeerId;
}

#[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default)]
enum GameState {
    #[default]
    AssetLoading,
    Matchmaking,
    InGame,
}

#[derive(Resource)]
struct LocalPlayerHandle(usize);

fn main() {
    App::new()
        .add_state::<GameState>()
        .add_loading_state(
            LoadingState::new(GameState::AssetLoading).continue_to_state(GameState::Matchmaking),
        )
        .add_collection_to_loading_state::<_, ImageAssets>(GameState::AssetLoading)
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                // fill the entire browser window
                fit_canvas_to_parent: true,
                ..default()
            }),
            ..default()
        }))
        .add_ggrs_plugin(
            GgrsPlugin::<GgrsConfig>::new()
                .with_input_system(input)
                .register_rollback_component::<Transform>()
                .register_rollback_component::<BulletReady>()
                .register_rollback_component::<MoveDir>(),
        )
        .insert_resource(ClearColor(Color::rgb(0.53, 0.53, 0.53)))
        .add_systems(
            OnEnter(GameState::Matchmaking),
            (setup, start_matchbox_socket),
        )
        .add_systems(OnEnter(GameState::InGame), spawn_players)
        .add_systems(
            Update,
            (
                wait_for_players.run_if(in_state(GameState::Matchmaking)),
                camera_follow.run_if(in_state(GameState::InGame)),
            ),
        )
        .add_systems(
            GgrsSchedule,
            (
                move_players,
                reload_bullet,
                fire_bullets.after(move_players).after(reload_bullet),
                move_bullet.after(fire_bullets),
                kill_players.after(move_bullet).after(move_players),
            ),
        )
        .run();
}

const MAP_SIZE: i32 = 41;
const GRID_WIDTH: f32 = 0.05;

#[derive(AssetCollection, Resource)]
struct ImageAssets {
    #[asset(path = "bullet.png")]
    bullet: Handle<Image>,
}

fn setup(mut commands: Commands) {
    // Horizontal lines
    for i in 0..=MAP_SIZE {
        commands.spawn(SpriteBundle {
            transform: Transform::from_translation(Vec3::new(
                0.,
                i as f32 - MAP_SIZE as f32 / 2.,
                0.,
            )),
            sprite: Sprite {
                color: Color::rgb(0.27, 0.27, 0.27),
                custom_size: Some(Vec2::new(MAP_SIZE as f32, GRID_WIDTH)),
                ..default()
            },
            ..default()
        });
    }

    // Vertical lines
    for i in 0..=MAP_SIZE {
        commands.spawn(SpriteBundle {
            transform: Transform::from_translation(Vec3::new(
                i as f32 - MAP_SIZE as f32 / 2.,
                0.,
                0.,
            )),
            sprite: Sprite {
                color: Color::rgb(0.27, 0.27, 0.27),
                custom_size: Some(Vec2::new(GRID_WIDTH, MAP_SIZE as f32)),
                ..default()
            },
            ..default()
        });
    }

    let mut camera_bundle = Camera2dBundle::default();
    camera_bundle.projection.scaling_mode = ScalingMode::FixedVertical(10.);
    commands.spawn(camera_bundle);
}

fn spawn_players(mut commands: Commands) {
    info!("Spawning players");

    // Player 1
    commands
        .spawn((
            Player { handle: 0 },
            BulletReady(true),
            MoveDir(-Vec2::X),
            SpriteBundle {
                transform: Transform::from_translation(Vec3::new(-2., 0., 100.)),
                sprite: Sprite {
                    color: Color::rgb(0., 0.47, 1.),
                    custom_size: Some(Vec2::new(1., 1.)),
                    ..default()
                },
                ..default()
            },
        ))
        .add_rollback();

    // Player 2
    commands
        .spawn((
            Player { handle: 1 },
            BulletReady(true),
            MoveDir(-Vec2::X),
            SpriteBundle {
                transform: Transform::from_translation(Vec3::new(2., 0., 100.)),
                sprite: Sprite {
                    color: Color::rgb(0., 0.4, 0.),
                    custom_size: Some(Vec2::new(1., 1.)),
                    ..default()
                },
                ..default()
            },
        ))
        .add_rollback();
}

fn start_matchbox_socket(mut commands: Commands) {
    let room_url = "ws://127.0.0.1:3536/extreme_bevy?next=2";
    info!("connecting to matchbox server: {room_url}");
    commands.insert_resource(MatchboxSocket::new_ggrs(room_url));
}

fn wait_for_players(
    mut commands: Commands,
    mut socket: ResMut<MatchboxSocket<SingleChannel>>,
    mut next_state: ResMut<NextState<GameState>>,
) {
    if socket.get_channel(0).is_err() {
        return; // we've already started
    }

    // Check for new connections
    socket.update_peers();
    let players = socket.players();

    let num_players = 2;
    if players.len() < num_players {
        return; // wait for more players
    }

    info!("All peers have joined, going in-game");

    // create a GGRS P2P session
    let mut session_builder = ggrs::SessionBuilder::<GgrsConfig>::new()
        .with_num_players(num_players)
        .with_input_delay(2);

    for (i, player) in players.into_iter().enumerate() {
        if player == PlayerType::Local {
            commands.insert_resource(LocalPlayerHandle(i));
        }

        session_builder = session_builder
            .add_player(player, i)
            .expect("failed to add player");
    }

    // move the channel out of the socket (required because GGRS takes ownership of it)
    let socket = socket.take_channel(0).unwrap();

    // start the GGRS session
    let ggrs_session = session_builder
        .start_p2p_session(socket)
        .expect("failed to start session");

    commands.insert_resource(bevy_ggrs::Session::P2P(ggrs_session));
    next_state.set(GameState::InGame);
}

fn move_players(
    inputs: Res<PlayerInputs<GgrsConfig>>,
    mut player_query: Query<(&mut Transform, &mut MoveDir, &Player)>,
) {
    for (mut transform, mut move_direction, player) in player_query.iter_mut() {
        let (input, _) = inputs[player.handle];
        let direction = direction(input);

        if direction == Vec2::ZERO {
            continue;
        }

        move_direction.0 = direction;

        let move_speed = 0.13;
        let move_delta = direction * move_speed;

        let old_pos = transform.translation.xy();
        let limit = Vec2::splat(MAP_SIZE as f32 / 2. - 0.5);
        let new_pos = (old_pos + move_delta).clamp(-limit, limit);

        transform.translation.x = new_pos.x;
        transform.translation.y = new_pos.y;
    }
}

fn reload_bullet(
    inputs: Res<PlayerInputs<GgrsConfig>>,
    mut query: Query<(&mut BulletReady, &Player)>,
) {
    for (mut can_fire, player) in query.iter_mut() {
        let (input, _) = inputs[player.handle];
        if !fire(input) {
            can_fire.0 = true;
        }
    }
}

fn fire_bullets(
    mut commands: Commands,
    inputs: Res<PlayerInputs<GgrsConfig>>,
    images: Res<ImageAssets>,
    mut player_query: Query<(&Transform, &Player, &mut BulletReady, &MoveDir)>,
) {
    for (transform, player, mut bullet_ready, move_dir) in player_query.iter_mut() {
        let (input, _) = inputs[player.handle];
        if fire(input) && bullet_ready.0 {
            let player_pos = transform.translation.xy();
            let pos = player_pos + move_dir.0 * PLAYER_RADIUS + BULLET_RADIUS;
            commands
                .spawn((
                    Bullet,
                    *move_dir,
                    SpriteBundle {
                        transform: Transform::from_translation(pos.extend(200.))
                            .with_rotation(Quat::from_rotation_arc_2d(Vec2::X, move_dir.0)),
                        texture: images.bullet.clone(),
                        sprite: Sprite {
                            custom_size: Some(Vec2::new(0.3, 0.1)),
                            ..default()
                        },
                        ..default()
                    },
                ))
                .add_rollback();
            bullet_ready.0 = false;
        }
    }
}

fn move_bullet(mut query: Query<(&mut Transform, &MoveDir), With<Bullet>>) {
    for (mut transform, dir) in query.iter_mut() {
        let delta = (dir.0 * 0.35).extend(0.);
        transform.translation += delta;
    }
}

const PLAYER_RADIUS: f32 = 0.5;
const BULLET_RADIUS: f32 = 0.025;

fn kill_players(
    mut commands: Commands,
    player_query: Query<(Entity, &Transform), (With<Player>, Without<Bullet>)>,
    bullet_query: Query<&Transform, With<Bullet>>,
) {
    for (player, player_transform) in player_query.iter() {
        for bullet_transform in bullet_query.iter() {
            let distance = Vec2::distance(
                player_transform.translation.xy(),
                bullet_transform.translation.xy(),
            );
            if distance < PLAYER_RADIUS + BULLET_RADIUS {
                commands.entity(player).despawn_recursive();
            }
        }
    }
}

fn camera_follow(
    player_handle: Option<Res<LocalPlayerHandle>>,
    player_query: Query<(&Player, &Transform)>,
    mut camera_query: Query<&mut Transform, (With<Camera>, Without<Player>)>,
) {
    let player_handle = match player_handle {
        Some(handle) => handle.0,
        None => return, // Session hasn't started yet
    };

    for (player, player_transform) in player_query.iter() {
        if player.handle != player_handle {
            continue;
        }

        let pos = player_transform.translation;

        for mut transform in camera_query.iter_mut() {
            transform.translation.x = pos.x;
            transform.translation.y = pos.y;
        }
    }
}


text:
[target.wasm32-unknown-unknown]
runner = "wasm-server-runner"

[build]
target = "wasm32-unknown-unknown"


text:
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">

Sorry, there's nothing at this address.

        </LayoutView>
    </NotFound>
</Router>

repo_name: Swimburger/BlazorGitHubPagesDemo

mime_type: text/html


text:
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
    <RazorLangVersion>3.0</RazorLangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.1" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.1" PrivateAssets="all" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.1" PrivateAssets="all" />
    <PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
  </ItemGroup>

</Project>


text:
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using BlazorGitHubPagesDemo
@using BlazorGitHubPagesDemo.Shared


text:
# BlazorGitHubPagesDemo

Sample repository for deploying ASP.NET Blazor WASM to GitHub Pages.
To learn how to deploy Blazor WASM to GitHub Pages, check out https://swimburger.net/blog/dotnet/how-to-deploy-aspnet-blazor-webassembly-to-github-pages.


text:
using System;
using System.Net.Http;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace BlazorGitHubPagesDemo
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("app");

            builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

            await builder.Build().RunAsync();
        }
    }
}


text:
@inherits LayoutComponentBase

    <NavMenu />

        About

        @Body


text:
    BlazorGitHubPagesDemo
    <button class="navbar-toggler" @onclick="ToggleNavMenu">

    </button>

            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                 Home
            </NavLink>

            <NavLink class="nav-link" href="counter">
                 Counter
            </NavLink>

            <NavLink class="nav-link" href="fetchdata">
                 Fetch data
            </NavLink>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}


text:
    @Title

        Please take our
        brief survey

    and tell us what you think.

@code {
    // Demonstrates how a parent component can supply parameters
    [Parameter]
    public string Title { get; set; }
}


text:
name: Deploy to GitHub Pages

# Run workflow on every push to the master branch
on:
  push:
    branches: [ master ]

jobs:
  deploy-to-github-pages:
    # use ubuntu-latest image to run steps on
    runs-on: ubuntu-latest
    steps:
    # uses GitHub's checkout action to checkout code form the master branch
    - uses: actions/checkout@v2

    # sets up .NET Core SDK 3.1
    - name: Setup .NET Core SDK
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1

    # publishes Blazor project to the release-folder
    - name: Publish .NET Core Project
      run: dotnet publish BlazorGitHubPagesDemo.csproj -c Release -o release --nologo

    # changes the base-tag in index.html from '/' to 'BlazorGitHubPagesDemo' to match GitHub Pages repository subdirectory
    - name: Change base-tag in index.html from / to BlazorGitHubPagesDemo
      run: sed -i 's/<base href="\/" \/>/<base href="\/BlazorGitHubPagesDemo\/" \/>/g' release/wwwroot/index.html

    # copy index.html to 404.html to serve the same file when a file is not found
    - name: copy index.html to 404.html
      run: cp release/wwwroot/index.html release/wwwroot/404.html

    # add .nojekyll file to tell GitHub pages to not treat this as a Jekyll project. (Allow files and folders starting with an underscore)
    - name: Add .nojekyll file
      run: touch release/wwwroot/.nojekyll

    - name: Commit wwwroot to GitHub Pages
      uses: JamesIves/github-pages-deploy-action@3.7.1
      with:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        BRANCH: gh-pages
        FOLDER: release/wwwroot


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorGitHubPagesDemo</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />

    <app>Loading...</app>

        An unhandled error has occurred.
        Reload
        🗙