diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b5ce96d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+build/
+node_modules/
+
+*.swp
+*.swo
+*.bak
+
+.DS_Store
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..cfec10b
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,19 @@
+Copyright (c) 2016 Griffin Moe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c2cb177
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+# In C - Score Progress
+
+This is a simple Electron app for displaying score progress on a projector
+during laptop performances of [Terry Riley's *In C*][inC]. It is designed to be
+used with [Max][max], but it could be used with other software (let me know if
+you do!). Supports Windows, macOS, and Linux.
+
+
+
+## Usage
+
+This app works by having each performer report a identification number unique
+to their computer and the phrase they are currently performing. In Max this is
+sent as a list of two integers, specifically the ID number followed by the
+phrase number. All messages should be sent to port number `41234` on the
+computer hosting this app. Below is an example Max patch that sends a message
+to the correct port on your local machine, assuming you are hosting the app:
+
+
+
+## Downloads
+
+TODO
+
+## About
+
+Created for the Loyola University (Chicago) Technology Ensemble. Fall 2016
+
+[License](LICENSE.md)
+
+[inC]: https://en.wikipedia.org/wiki/In_C
+[max]: https://cycling74.com/max7/
diff --git a/app/index.html b/app/index.html
new file mode 100644
index 0000000..ceb8fa5
--- /dev/null
+++ b/app/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+ In C - Score Progress
+
+
+
+
+
+
+
diff --git a/app/js/renderer.js b/app/js/renderer.js
new file mode 100644
index 0000000..61ddc8b
--- /dev/null
+++ b/app/js/renderer.js
@@ -0,0 +1,48 @@
+'use strict';
+
+const d3 = require('d3');
+
+const NUM_PHRASES = 53;
+
+const addPhraseSection = (parent, phraseNum) => {
+ parent.append('section')
+ .attr('class', 'phrase-section')
+ .append('img')
+ .attr('src', `phrases/Sco${phraseNum}.png`);
+}
+
+const colorById = (cid) => {
+ const hue = (cid * 100) % 355;
+ return d3.hsl(hue, 0.4, 0.6);
+}
+
+module.exports.start = () => {
+ let body = d3.select('body');
+ for(let i=1; i <= NUM_PHRASES; ++i) {
+ addPhraseSection(body, i);
+ }
+}
+
+module.exports.update = (playerMatrix) => {
+ //Wipe existing player ids
+ d3.selectAll('section.phrase-section')
+ .selectAll('div.players').remove();
+
+ //Join list of players to list of phrases
+ let divPlayers = d3.selectAll('section.phrase-section')
+ .data(playerMatrix)
+ .append('div')
+ .attr('class', 'players');
+
+ //Join players to phrase
+ let playerIds = divPlayers.selectAll('span')
+ .data((d) => { return d; })
+ .enter()
+ .insert('span')
+ .attr('class', 'player')
+ .style('background-color', (d) => {
+ return colorById(d).toString();
+ })
+ .insert('span')
+ .text((d) => { return d; });
+}
diff --git a/app/js/server.js b/app/js/server.js
new file mode 100644
index 0000000..c4a13e4
--- /dev/null
+++ b/app/js/server.js
@@ -0,0 +1,69 @@
+'use strict';
+
+const NUM_PHRASES = 53;
+
+module.exports = (renderer) => {
+
+ const dgram = require('dgram');
+ const server = dgram.createSocket('udp4');
+
+ var playerMatrix = (() => {
+ let matrix = [ ];
+ for(let i=0; i < NUM_PHRASES; ++i) {
+ matrix[i] = [];
+ }
+ return matrix;
+ })();
+
+ const arraySort = (a,b) => { return a - b; }
+
+ const updateMatrix = (matrix,data) => {
+ const cidExists = matrix.reduce((a,b) => {
+ let index = Math.max(a.index, b.indexOf(data.cid));
+ return {
+ "phrase": (index==-1) ? a.phrase+1 : a.phrase,
+ "index": index
+ };
+ }, {"phrase": 0, "index": -1});
+
+ if(cidExists.index == -1) {
+ matrix[data.phrase].push(data.cid);
+ matrix[data.phrase].sort(arraySort);
+ } else {
+ matrix[cidExists.phrase].splice(cidExists.index, 1);
+ matrix[cidExists.phrase].sort(arraySort);
+ matrix[data.phrase].push(data.cid);
+ matrix[data.phrase].sort(arraySort);
+ }
+
+ }
+
+ const processMaxListPacket = (buffer) => {
+ let data = buffer.slice(12);
+ let cid = data.readInt32BE();
+ let phrase = data.readInt32BE(4);
+ return { 'cid': cid, 'phrase': phrase-1 };
+ }
+
+ server.on('error', (err) => {
+ console.log(`server error:\n${err.stack}`);
+ server.close();
+ });
+
+ server.on('message', (msg, rinfo) => {
+ let data = processMaxListPacket(msg);
+ console.log(`server got: [${data.cid} ${data.phrase}] from ${rinfo.address}:${rinfo.port}`);
+
+ updateMatrix(playerMatrix, data);
+ renderer.update(playerMatrix);
+ });
+
+ server.on('listening', () => {
+ let address = server.address();
+ console.log(`server listening on: ${address.address}:${address.port}`);
+ renderer.start();
+ });
+
+ server.bind(41234); // server listening 0.0.0.0:41234
+
+}
diff --git a/app/phrases/Sco1.png b/app/phrases/Sco1.png
new file mode 100644
index 0000000..085ea12
Binary files /dev/null and b/app/phrases/Sco1.png differ
diff --git a/app/phrases/Sco10.png b/app/phrases/Sco10.png
new file mode 100644
index 0000000..450d8fd
Binary files /dev/null and b/app/phrases/Sco10.png differ
diff --git a/app/phrases/Sco11.png b/app/phrases/Sco11.png
new file mode 100644
index 0000000..1d83db8
Binary files /dev/null and b/app/phrases/Sco11.png differ
diff --git a/app/phrases/Sco12.png b/app/phrases/Sco12.png
new file mode 100644
index 0000000..e646858
Binary files /dev/null and b/app/phrases/Sco12.png differ
diff --git a/app/phrases/Sco13.png b/app/phrases/Sco13.png
new file mode 100644
index 0000000..940917f
Binary files /dev/null and b/app/phrases/Sco13.png differ
diff --git a/app/phrases/Sco14.png b/app/phrases/Sco14.png
new file mode 100644
index 0000000..345ec33
Binary files /dev/null and b/app/phrases/Sco14.png differ
diff --git a/app/phrases/Sco15.png b/app/phrases/Sco15.png
new file mode 100644
index 0000000..f55e281
Binary files /dev/null and b/app/phrases/Sco15.png differ
diff --git a/app/phrases/Sco16.png b/app/phrases/Sco16.png
new file mode 100644
index 0000000..7cb09dd
Binary files /dev/null and b/app/phrases/Sco16.png differ
diff --git a/app/phrases/Sco17.png b/app/phrases/Sco17.png
new file mode 100644
index 0000000..8bde1f6
Binary files /dev/null and b/app/phrases/Sco17.png differ
diff --git a/app/phrases/Sco18.png b/app/phrases/Sco18.png
new file mode 100644
index 0000000..b70a422
Binary files /dev/null and b/app/phrases/Sco18.png differ
diff --git a/app/phrases/Sco19.png b/app/phrases/Sco19.png
new file mode 100644
index 0000000..a35ab73
Binary files /dev/null and b/app/phrases/Sco19.png differ
diff --git a/app/phrases/Sco2.png b/app/phrases/Sco2.png
new file mode 100644
index 0000000..8fdab59
Binary files /dev/null and b/app/phrases/Sco2.png differ
diff --git a/app/phrases/Sco20.png b/app/phrases/Sco20.png
new file mode 100644
index 0000000..c8b7cf9
Binary files /dev/null and b/app/phrases/Sco20.png differ
diff --git a/app/phrases/Sco21.png b/app/phrases/Sco21.png
new file mode 100644
index 0000000..0c94e63
Binary files /dev/null and b/app/phrases/Sco21.png differ
diff --git a/app/phrases/Sco22.png b/app/phrases/Sco22.png
new file mode 100644
index 0000000..86c14b5
Binary files /dev/null and b/app/phrases/Sco22.png differ
diff --git a/app/phrases/Sco23.png b/app/phrases/Sco23.png
new file mode 100644
index 0000000..99764b3
Binary files /dev/null and b/app/phrases/Sco23.png differ
diff --git a/app/phrases/Sco24.png b/app/phrases/Sco24.png
new file mode 100644
index 0000000..1219c2a
Binary files /dev/null and b/app/phrases/Sco24.png differ
diff --git a/app/phrases/Sco25.png b/app/phrases/Sco25.png
new file mode 100644
index 0000000..a5e67da
Binary files /dev/null and b/app/phrases/Sco25.png differ
diff --git a/app/phrases/Sco26.png b/app/phrases/Sco26.png
new file mode 100644
index 0000000..1806b70
Binary files /dev/null and b/app/phrases/Sco26.png differ
diff --git a/app/phrases/Sco27.png b/app/phrases/Sco27.png
new file mode 100644
index 0000000..c35bef6
Binary files /dev/null and b/app/phrases/Sco27.png differ
diff --git a/app/phrases/Sco28.png b/app/phrases/Sco28.png
new file mode 100644
index 0000000..332f53b
Binary files /dev/null and b/app/phrases/Sco28.png differ
diff --git a/app/phrases/Sco29.png b/app/phrases/Sco29.png
new file mode 100644
index 0000000..3d6c576
Binary files /dev/null and b/app/phrases/Sco29.png differ
diff --git a/app/phrases/Sco3.png b/app/phrases/Sco3.png
new file mode 100644
index 0000000..65a6433
Binary files /dev/null and b/app/phrases/Sco3.png differ
diff --git a/app/phrases/Sco30.png b/app/phrases/Sco30.png
new file mode 100644
index 0000000..1e5b189
Binary files /dev/null and b/app/phrases/Sco30.png differ
diff --git a/app/phrases/Sco31.png b/app/phrases/Sco31.png
new file mode 100644
index 0000000..7dd3bf6
Binary files /dev/null and b/app/phrases/Sco31.png differ
diff --git a/app/phrases/Sco32.png b/app/phrases/Sco32.png
new file mode 100644
index 0000000..d4b6cf0
Binary files /dev/null and b/app/phrases/Sco32.png differ
diff --git a/app/phrases/Sco33.png b/app/phrases/Sco33.png
new file mode 100644
index 0000000..7133ad5
Binary files /dev/null and b/app/phrases/Sco33.png differ
diff --git a/app/phrases/Sco34.png b/app/phrases/Sco34.png
new file mode 100644
index 0000000..e626a53
Binary files /dev/null and b/app/phrases/Sco34.png differ
diff --git a/app/phrases/Sco35.png b/app/phrases/Sco35.png
new file mode 100644
index 0000000..0c45f94
Binary files /dev/null and b/app/phrases/Sco35.png differ
diff --git a/app/phrases/Sco36.png b/app/phrases/Sco36.png
new file mode 100644
index 0000000..9b34c29
Binary files /dev/null and b/app/phrases/Sco36.png differ
diff --git a/app/phrases/Sco37.png b/app/phrases/Sco37.png
new file mode 100644
index 0000000..99ed3e5
Binary files /dev/null and b/app/phrases/Sco37.png differ
diff --git a/app/phrases/Sco38.png b/app/phrases/Sco38.png
new file mode 100644
index 0000000..6881c75
Binary files /dev/null and b/app/phrases/Sco38.png differ
diff --git a/app/phrases/Sco39.png b/app/phrases/Sco39.png
new file mode 100644
index 0000000..b8d4004
Binary files /dev/null and b/app/phrases/Sco39.png differ
diff --git a/app/phrases/Sco4.png b/app/phrases/Sco4.png
new file mode 100644
index 0000000..131813f
Binary files /dev/null and b/app/phrases/Sco4.png differ
diff --git a/app/phrases/Sco40.png b/app/phrases/Sco40.png
new file mode 100644
index 0000000..6bd96d3
Binary files /dev/null and b/app/phrases/Sco40.png differ
diff --git a/app/phrases/Sco41.png b/app/phrases/Sco41.png
new file mode 100644
index 0000000..e8a2618
Binary files /dev/null and b/app/phrases/Sco41.png differ
diff --git a/app/phrases/Sco42.png b/app/phrases/Sco42.png
new file mode 100644
index 0000000..eb8cd27
Binary files /dev/null and b/app/phrases/Sco42.png differ
diff --git a/app/phrases/Sco43.png b/app/phrases/Sco43.png
new file mode 100644
index 0000000..3ab2832
Binary files /dev/null and b/app/phrases/Sco43.png differ
diff --git a/app/phrases/Sco44.png b/app/phrases/Sco44.png
new file mode 100644
index 0000000..b087bb4
Binary files /dev/null and b/app/phrases/Sco44.png differ
diff --git a/app/phrases/Sco45.png b/app/phrases/Sco45.png
new file mode 100644
index 0000000..e293b28
Binary files /dev/null and b/app/phrases/Sco45.png differ
diff --git a/app/phrases/Sco46.png b/app/phrases/Sco46.png
new file mode 100644
index 0000000..f79f5be
Binary files /dev/null and b/app/phrases/Sco46.png differ
diff --git a/app/phrases/Sco47.png b/app/phrases/Sco47.png
new file mode 100644
index 0000000..c3b3117
Binary files /dev/null and b/app/phrases/Sco47.png differ
diff --git a/app/phrases/Sco48.png b/app/phrases/Sco48.png
new file mode 100644
index 0000000..2f1fa2d
Binary files /dev/null and b/app/phrases/Sco48.png differ
diff --git a/app/phrases/Sco49.png b/app/phrases/Sco49.png
new file mode 100644
index 0000000..49b86b7
Binary files /dev/null and b/app/phrases/Sco49.png differ
diff --git a/app/phrases/Sco5.png b/app/phrases/Sco5.png
new file mode 100644
index 0000000..224b330
Binary files /dev/null and b/app/phrases/Sco5.png differ
diff --git a/app/phrases/Sco50.png b/app/phrases/Sco50.png
new file mode 100644
index 0000000..74b4c55
Binary files /dev/null and b/app/phrases/Sco50.png differ
diff --git a/app/phrases/Sco51.png b/app/phrases/Sco51.png
new file mode 100644
index 0000000..38ab5bb
Binary files /dev/null and b/app/phrases/Sco51.png differ
diff --git a/app/phrases/Sco52.png b/app/phrases/Sco52.png
new file mode 100644
index 0000000..d9c2a92
Binary files /dev/null and b/app/phrases/Sco52.png differ
diff --git a/app/phrases/Sco53.png b/app/phrases/Sco53.png
new file mode 100644
index 0000000..2f1b300
Binary files /dev/null and b/app/phrases/Sco53.png differ
diff --git a/app/phrases/Sco6.png b/app/phrases/Sco6.png
new file mode 100644
index 0000000..3f972d1
Binary files /dev/null and b/app/phrases/Sco6.png differ
diff --git a/app/phrases/Sco7.png b/app/phrases/Sco7.png
new file mode 100644
index 0000000..c968894
Binary files /dev/null and b/app/phrases/Sco7.png differ
diff --git a/app/phrases/Sco8.png b/app/phrases/Sco8.png
new file mode 100644
index 0000000..5d9df70
Binary files /dev/null and b/app/phrases/Sco8.png differ
diff --git a/app/phrases/Sco9.png b/app/phrases/Sco9.png
new file mode 100644
index 0000000..c7bac66
Binary files /dev/null and b/app/phrases/Sco9.png differ
diff --git a/app/style.css b/app/style.css
new file mode 100644
index 0000000..6e73c49
--- /dev/null
+++ b/app/style.css
@@ -0,0 +1,46 @@
+body {
+ font-family: Helvetica, Arial, sans-serif;
+ font-weight: 500;
+ background-color: #ddd;
+ color: white;
+}
+
+.phrase-section {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 30px;
+ padding: 15px;
+ background-color: #fff;
+ box-shadow: 2px 3px 5px #999;
+}
+
+.phrase-section > img {
+ max-width: 100%;
+ height: auto;
+ margin: 5px;
+}
+
+.players {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.player {
+ display: flex;
+ float: left;
+ font-size: 1.25rem;
+ margin: 5px;
+ padding: 5px;
+ width: 30px;
+ height: 30px;
+}
+
+.player > span {
+ width: 100%;
+ align-self: center;
+ text-align: center;
+}
+
+::-webkit-scrollbar {
+ display: none;
+}
diff --git a/img/max-patch.png b/img/max-patch.png
new file mode 100644
index 0000000..7212d59
Binary files /dev/null and b/img/max-patch.png differ
diff --git a/img/screenshot.png b/img/screenshot.png
new file mode 100644
index 0000000..1678901
Binary files /dev/null and b/img/screenshot.png differ
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..18a4773
--- /dev/null
+++ b/main.js
@@ -0,0 +1,29 @@
+const electron = require('electron')
+const app = electron.app
+const BrowserWindow = electron.BrowserWindow
+
+const path = require('path')
+const url = require('url')
+
+let mainWindow
+
+app.on('ready', () => {
+ mainWindow = new BrowserWindow({
+ width: 1200,
+ height: 800,
+ })
+
+ mainWindow.loadURL(url.format({
+ pathname: path.join(__dirname, 'app/index.html'),
+ protocol: 'file:',
+ slashes: true
+ }))
+
+ mainWindow.on('closed', () => {
+ mainWindow = null
+ })
+})
+
+app.on('window-all-closed', () => {
+ app.quit()
+})
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..16cf146
--- /dev/null
+++ b/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "in-c-score-progress",
+ "productName": "InCScoreProgress",
+ "version": "1.0.0",
+ "description": "App for displaying performers' progress of Terry Riley's \"In C\".",
+ "keywords": ["electron", "max", "msp", "Max 7", "In C", "Terry Riley"],
+ "main": "main.js",
+ "scripts": {
+ "start": "electron .",
+ "app": "electron-packager . --all --out build/"
+ },
+ "author": "Griffin Moe",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/gmoe/in-c-score-progress.git"
+ },
+ "bugs": {
+ "url": "https://github.com/gmoe/my_package/issues"
+ },
+ "homepage": "https://github.com/gmoe/in-c-score-progress",
+ "dependencies": {
+ "d3": "^4.3.0",
+ "electron": "^1.4.7"
+ },
+ "devDependencies": {
+ "electron-packager": "^8.3.0"
+ }
+}