diff --git a/brickbreak/brickbreakclasses.js b/brickbreak/brickbreakclasses.js
index 08f47fc..c296b98 100644
--- a/brickbreak/brickbreakclasses.js
+++ b/brickbreak/brickbreakclasses.js
@@ -22,6 +22,7 @@ export class Hud {
export class Ball {
constructor(world) {
this.canvas = world.canvas;
+ this.ctx = world.ctx;
this.x = this.canvas.width / 2;
this.y = this.canvas.height - 30;
this.ballRadius = 10;
diff --git a/index.js b/index.js
index 5b944de..5a14c0c 100644
--- a/index.js
+++ b/index.js
@@ -1,5 +1,3 @@
-const brickBreakClasses = require('./brickbreak/brickbreakclasses');
-const snakeClasses = require('./snake/snakeclasses');
+const pongserverclasses = require('./pongserver/pongserverclasses');
-module.exports.brickbreak = brickBreakClasses;
-module.exports.snake = snakeClasses;
\ No newline at end of file
+module.exports.pongserver = pongserverclasses;
diff --git a/pongserver/README.md b/pongserver/README.md
new file mode 100644
index 0000000..1e51a4e
--- /dev/null
+++ b/pongserver/README.md
@@ -0,0 +1,111 @@
+## USAGE
+An example setup could look like this:
+```
+test-game
+| public
+----| example.html
+----| example.js
+| index.js
+```
+
+First, `npm install --save game-iverse` and `npm install --save socket.io`. Then our source could look like this:
+
+```example.html```
+```html
+
+
+
+```
+
+```example.js```
+```js
+import { World, Ball, Paddle, Hud } from '/pongserver/pongclasses.js';
+var world = new World(document.getElementById("canvas"));
+var ball = new Ball(world);
+var hud = new Hud(world);
+var p1paddle = new Paddle(world);
+var p2paddle = new Paddle(world);
+p1paddle.keyUp;
+p1paddle.keyDown;
+var lastUpdate = performance.now();
+var socket = io('', {query: `width=${world.canvas.width}&height=${world.canvas.height}`});
+socket.on('update', function(state) {
+ p1paddle.setState(state.p1paddle);
+ p2paddle.setState(state.p2paddle);
+ ball.setState(state.ball);
+ hud.setState(state.hud);
+});
+var draw = (time) => {
+ if (performance.now() - lastUpdate >= 30) {
+ lastUpdate = performance.now();
+ socket.emit('update', {p1paddle: p1paddle.getState()});
+ }
+ world.ctx.clearRect(0, 0, world.canvas.width, world.canvas.height);
+ ball.draw();
+ p1paddle.draw();
+ p2paddle.draw();
+ hud.draw();
+ requestAnimationFrame(draw);
+}
+requestAnimationFrame(draw);
+```
+
+```index.js```
+```js
+const express = require('express');
+const app = express();
+const http = require('http');
+const server = http.Server(app);
+const path = require('path');
+const io = require('socket.io')(server);
+const gameiverse = require('../Game-iverse')
+const port = 8000;
+
+app.set('port', port);
+app.get('/', function(req, res) {
+ res.sendFile(path.join(__dirname, 'public/example.html'));
+});
+app.use(express.static('public'))
+app.use(express.static(path.join(__dirname, './node_modules/game-iverse')))
+
+io.on('connection', (socket) => {
+ var world = new gameiverse.pongserver.World(socket.handshake.query.width, socket.handshake.query.height);
+ var ball = new gameiverse.pongserver.Ball(world);
+ var p1paddle = new gameiverse.pongserver.Paddle(world, 'player1');
+ var p2paddle = new gameiverse.pongserver.AIPaddle(world, 'player2', ball);
+ var hud = new gameiverse.pongserver.Hud();
+
+ socket.on('update', (state) => {
+ p1paddle.setState(state.p1paddle);
+ });
+ var tc = 0;
+ var tick = () => {
+ p1paddle.movement();
+ p2paddle.movement();
+ ball.boundaries([p1paddle, p2paddle], hud);
+ ball.movement();
+ if (tc >= 6) {
+ socket.emit('update', {
+ p1paddle: p1paddle.getState(),
+ p2paddle: p2paddle.getState(),
+ hud: hud.getState(),
+ ball: ball.getState()
+ });
+ tc = 0;
+ return;
+ }
+ tc += 1;
+ }
+
+ var iv = setInterval(tick, 5);
+
+ socket.on('disconnect', () => {
+ clearInterval(iv);
+ });
+});
+
+server.listen(port, function() {
+});
+```
+
+You should now be able to run the game by visiting `localhost:8000`.
diff --git a/pongserver/pongclasses.js b/pongserver/pongclasses.js
new file mode 100644
index 0000000..2dc977d
--- /dev/null
+++ b/pongserver/pongclasses.js
@@ -0,0 +1,113 @@
+export class World {
+ constructor(canvas) {
+ this.canvas = canvas;
+ this.ctx = this.canvas.getContext("2d");
+ }
+}
+
+export class Hud {
+ constructor(world) {
+ this.canvas = world.canvas;
+ this.ctx = world.ctx;
+ this.p1score = 0;
+ this.p2score = 0;
+ }
+
+ draw() {
+ this.ctx.font = "16px Arial";
+ this.ctx.fillStyle = "#0095DD";
+ this.ctx.fillText("Player 1 Score: " + this.p1score + ", Player 2 Score: " + this.p2score, 8, 20);
+ }
+
+ setState(state) {
+ this.p1score = state.p1score;
+ this.p2score = state.p2score;
+ }
+}
+
+export class Paddle {
+ constructor(world) {
+ this.canvas = world.canvas;
+ this.ctx = world.ctx;
+ this.height = 75;
+ this.width = 10;
+ this.y = 0;
+ this.x = 0;
+ this.upPressed = false;
+ this.downPressed = false;
+
+ this.keyDown = document.addEventListener(
+ "keydown",
+ e => this.keyDownHandler(e),
+ false
+ );
+
+ this.keyUp = document.addEventListener(
+ "keyup",
+ e => this.keyUpHandler(e),
+ false
+ );
+ }
+ keyUpHandler(e) {
+ if (e.keyCode == 38) {
+ this.upPressed = false;
+ } else if (e.keyCode == 40) {
+ this.downPressed = false;
+ }
+ }
+
+ keyDownHandler(e) {
+ if (e.keyCode == 38) {
+ this.upPressed = true;
+ } else if (e.keyCode == 40) {
+ this.downPressed = true;
+ }
+ }
+ getState() {
+ return {
+ upPressed: this.upPressed,
+ downPressed: this.downPressed
+ };
+ }
+
+ setState(state) {
+ this.y = state.y;
+ this.x = state.x;
+ }
+
+ draw() {
+ this.ctx.beginPath();
+ this.ctx.rect(
+ this.x,
+ this.y,
+ this.width,
+ this.height
+ );
+ this.ctx.fillStyle = "#0095DD";
+ this.ctx.fill();
+ this.ctx.closePath();
+ }
+}
+
+export class Ball {
+ constructor(world) {
+ this.canvas = world.canvas;
+ this.ctx = world.ctx;
+ this.radius = 10;
+ this.x = 0;
+ this.y = 0;
+ }
+
+ draw() {
+ this.ctx.beginPath();
+ this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ this.ctx.fillStyle = "#0095DD";
+ this.ctx.fill();
+ this.ctx.closePath();
+ }
+
+ setState(state) {
+ this.x = state.x;
+ this.y = state.y;
+ }
+}
diff --git a/pongserver/pongserverclasses.js b/pongserver/pongserverclasses.js
new file mode 100644
index 0000000..e83e1a5
--- /dev/null
+++ b/pongserver/pongserverclasses.js
@@ -0,0 +1,164 @@
+class World {
+ constructor(width, height) {
+ this.width = width;
+ this.height = height;
+ }
+}
+
+class Hud {
+ constructor() {
+ this.p1score = 0;
+ this.p2score = 0;
+ }
+
+ getState() {
+ return {
+ p1score: this.p1score,
+ p2score: this.p2score
+ }
+ }
+}
+
+class Ball {
+ constructor(world) {
+ this.world = world;
+ this.radius = 10;
+ this.reset();
+ }
+
+ reset() {
+ this.x = this.world.width / 2;
+ this.y = this.world.height / 2;
+ this.dx = 1;
+ this.dy = -1;
+ this.speed = 25;
+ this.lastUpdate = process.hrtime.bigint();
+ }
+
+ nextPos() {
+ var dt = Number(process.hrtime.bigint() - this.lastUpdate) / 1e9;
+ return {
+ x: this.x + this.dx * this.speed * dt,
+ y: this.y + this.dy * this.speed * dt
+ }
+ }
+
+ boundaries(paddles, hud) {
+ const clamp = (v, min, max) => v < min? min: v > max? max: v;
+ var nextPos = this.nextPos();
+ if (nextPos.y - this.radius <= 0 || nextPos.y + this.radius >= this.world.height) {
+ // collision with top or bottom of play area
+ this.dy = -this.dy;
+ }
+ for (var paddle of paddles) {
+ var dx = nextPos.x - clamp(nextPos.x, paddle.x, paddle.x + paddle.width);
+ var dy = nextPos.y - clamp(nextPos.y, paddle.y, paddle.y + paddle.height);
+ if (dx**2 + dy**2 < this.radius**2) {
+ // collision with a paddle
+ this.dx = -this.dx;
+ return;
+ }
+ }
+ if (nextPos.x - this.radius <= 0) {
+ hud.p2score += 1;
+ this.reset();
+ }
+ else if (nextPos.x + this.radius >= this.world.width) {
+ hud.p1score += 1;
+ this.reset();
+ }
+ }
+
+ movement() {
+ var nextPos = this.nextPos();
+ this.lastUpdate = process.hrtime.bigint();
+ this.x = nextPos.x;
+ this.y = nextPos.y;
+ this.speed += this.speed*0.01;
+ }
+
+ getState() {
+ return {
+ x: this.x,
+ y: this.y
+ };
+ }
+}
+
+class Paddle {
+ constructor(world, player) {
+ this.world = world;
+ this.height = 75;
+ this.width = 10;
+ this.x = player == "player1"? 0: this.world.width - this.width;
+ this.speed = 100;
+ this.reset();
+ }
+
+ reset() {
+ this.lastUpdate = process.hrtime.bigint();
+ this.upPressed = false;
+ this.downPressed = false;
+ this.y = this.world.height - this.height / 2;
+ }
+
+ getState() {
+ return {
+ x: this.x,
+ y: this.y
+ }
+ }
+
+ setState(state) {
+ this.upPressed = state.upPressed;
+ this.downPressed = state.downPressed;
+ }
+
+ nextPos() {
+ var y = this.y;
+ var dt = Number(process.hrtime.bigint() - this.lastUpdate) / 1e9;
+ if (this.upPressed) {
+ y -= this.speed * dt;
+ }
+ else if (this.downPressed) {
+ y += this.speed * dt;
+ }
+ return {
+ x: this.x,
+ y: y
+ }
+ }
+
+ movement() {
+ var nextPos = this.nextPos();
+ this.lastUpdate = process.hrtime.bigint();
+ const clamp = (v, min, max) => v < min? min: v > max? max: v;
+ this.y = clamp(nextPos.y, 0, this.world.height - this.height);
+ }
+}
+
+class AIPaddle extends Paddle {
+ constructor(world, player, ball) {
+ super(world, player);
+ this.ball = ball;
+ }
+ nextPos() {
+ super.upPressed = false;
+ super.downPressed = false;
+ if (this.y + this.height / 2 > this.ball.y) {
+ super.upPressed = true;
+ }
+ else if (this.y + this.height / 2 < this.ball.y) {
+ super.downPressed = true;
+ }
+ return super.nextPos();
+ }
+}
+
+module.exports = {
+ AIPaddle,
+ Paddle,
+ Hud,
+ Ball,
+ World
+};