diff --git a/river.mp4 b/assets/river.mp4
similarity index 100%
rename from river.mp4
rename to assets/river.mp4
diff --git a/dragdrop.html b/dragdrop.html
index 21c493f..da4b1bd 100644
--- a/dragdrop.html
+++ b/dragdrop.html
@@ -1,11 +1,11 @@
-
- Cyclogram.js prototype
+ Cyclogram.js
+
-
-
-
Select a video or image file
-
-
-
-
-
-
+
+
+
Select a video or image file
+
+
+
diff --git a/dragdrop.js b/dragdrop.js
new file mode 100644
index 0000000..920fe00
--- /dev/null
+++ b/dragdrop.js
@@ -0,0 +1,51 @@
+import { createCyclogramCanvas, drawCyclogram } from "./modules/cyclogram.js";
+import { processUploadedVideo } from "./modules/video.js";
+
+let processedVideoResponse = {};
+let framesPerDay = 288/2; // remove nighttime hours, crudely
+let dotSize = 6;
+let i = 0;
+let rowNumber = 0;
+let colNumber = 0;
+
+new p5(function(p5) {
+ p5.setup = function() {
+ const canvasOptions = {
+ width: 1000,
+ height: 400,
+ element: 'canvas',
+ };
+ createCyclogramCanvas(p5, canvasOptions);
+
+ document.getElementById('input').addEventListener('change', async function(event) {
+ try {
+ processedVideoResponse = await processUploadedVideo(p5, event);
+ } catch (error) {
+ alert('Error loading file!');
+ }
+ })
+ }
+
+ p5.draw = function() {
+ const { video, frameRate, steps, density, x, y } = processedVideoResponse;
+ if (video) {
+ const drawOptions = {
+ video,
+ frameRate,
+ steps,
+ density,
+ myX: x || 100,
+ myY: y || 400,
+ framesPerDay,
+ dotSize,
+ i,
+ rowNumber,
+ colNumber,
+ };
+ const cyclogramResults = drawCyclogram(p5, drawOptions);
+ i = cyclogramResults.i;
+ rowNumber = cyclogramResults.rowNumber;
+ colNumber = cyclogramResults.colNumber;
+ }
+ }
+});
diff --git a/modules/cyclogram.js b/modules/cyclogram.js
new file mode 100644
index 0000000..7d02461
--- /dev/null
+++ b/modules/cyclogram.js
@@ -0,0 +1,36 @@
+function createCyclogramCanvas(p5Instance, canvasOptions) {
+ const { width, height, element } = canvasOptions;
+ const canvas = p5Instance.createCanvas(width, height);
+ canvas.parent(element);
+ return canvas;
+}
+
+function drawCyclogram(p5Instance, drawOptions) {
+ const { video, frameRate, steps, density, myX, myY, framesPerDay, dotSize } = drawOptions;
+ let { i, rowNumber, colNumber } = drawOptions;
+
+ // video.elt.currentTime = i/frameRate;
+ video.loadPixels();
+ let color = [
+ video.pixels[(myY * video.width * 4 * density) + (myX * 4 * density)], // get red
+ video.pixels[(myY * video.width * 4 * density) + (myX * 4 * density + 1)], // get green
+ video.pixels[(myY * video.width * 4 * density) + (myX * 4 * density + 2)] // get blue
+ ];
+
+ p5Instance.rect(i % framesPerDay * dotSize, rowNumber * dotSize, dotSize, dotSize);
+ p5Instance.fill(color);
+ colNumber += 1;
+ if (i % framesPerDay == 0) {
+ rowNumber += 1; // new row
+ colNumber = 0;
+ }
+ if (i == steps) i = 0;
+ else i++;
+
+ return { i, rowNumber, colNumber };
+}
+
+export {
+ createCyclogramCanvas,
+ drawCyclogram,
+}
diff --git a/modules/video.js b/modules/video.js
new file mode 100644
index 0000000..bef7318
--- /dev/null
+++ b/modules/video.js
@@ -0,0 +1,54 @@
+function getCoordinatesOfVideoClick(video, clickEvent) {
+ const x = parseInt(clickEvent.offsetX/video.elt.clientWidth * video.width);
+ const y = parseInt(clickEvent.offsetY/video.elt.clientHeight * video.height);
+ clickEvent.preventDefault();
+ return { x, y };
+}
+
+function loadVideo(p5Instance, loadOptions) {
+ const { url, element } = loadOptions;
+ const video = p5Instance.createVideo(url);
+ video.parent(element);
+
+ // video.elt.getVideoPlaybackQuality().totalVideoFrames // only how many frames have loaded so far
+ video.elt.controls = true;
+ video.elt.load();
+ p5Instance.noStroke();
+ const frameRate = 30;
+ const steps = parseInt(frameRate * video.elt.duration) // in seconds
+ const density = 1; // pixelDensity();
+
+ let coordinates = {};
+ video.elt.onclick = function click(event) {
+ coordinates = getCoordinatesOfVideoClick(video, event);
+ };
+
+ return { video, frameRate, steps, density, ...coordinates };
+}
+
+async function processUploadedVideo(p5Instance, event) {
+ const file = event.target.files[0];
+ const fileReader = new FileReader();
+
+ return new Promise((resolve, reject) => {
+ fileReader.onerror = () => {
+ fileReader.abort();
+ reject(new DOMException('Problem parsing input file'));
+ };
+
+ fileReader.onload = () => {
+ const blob = new Blob([fileReader.result], { type: file.type });
+ const url = URL.createObjectURL(blob);
+ const loadOptions = { url, element: 'video' };
+ const response = loadVideo(p5Instance, loadOptions);
+ resolve(response);
+ };
+ fileReader.readAsArrayBuffer(file);
+ });
+}
+
+export {
+ loadVideo,
+ getCoordinatesOfVideoClick,
+ processUploadedVideo,
+};
diff --git a/prototype.html b/prototype.html
index 744fcbb..9b30808 100644
--- a/prototype.html
+++ b/prototype.html
@@ -1,4 +1,3 @@
-
@@ -6,6 +5,7 @@
Cyclogram.js prototype
+
-
-
-
-
-
+
+
diff --git a/prototype.js b/prototype.js
new file mode 100644
index 0000000..bf85036
--- /dev/null
+++ b/prototype.js
@@ -0,0 +1,47 @@
+import { createCyclogramCanvas, drawCyclogram } from "./modules/cyclogram.js";
+import { loadVideo } from "./modules/video.js";
+
+let processedVideoResponse = {};
+let framesPerDay = 288/2; // remove nighttime hours, crudely
+let dotSize = 6;
+let i = 0;
+let rowNumber = 0;
+let colNumber = 0;
+
+new p5(function(p5) {
+ p5.setup = function() {
+ const canvasOptions = {
+ width: 1000,
+ height: 400,
+ element: 'canvas',
+ };
+ createCyclogramCanvas(p5, canvasOptions);
+
+ const url = '../assets/river.mp4';
+ const loadOptions = { url, element: 'video' };
+ processedVideoResponse = loadVideo(p5, loadOptions);
+ }
+
+ p5.draw = function() {
+ const { video, frameRate, steps, density, x, y } = processedVideoResponse;
+ if (video) {
+ const drawOptions = {
+ video,
+ frameRate,
+ steps,
+ density,
+ myX: x || 100,
+ myY: y || 400,
+ framesPerDay,
+ dotSize,
+ i,
+ rowNumber,
+ colNumber,
+ };
+ const cyclogramResults = drawCyclogram(p5, drawOptions);
+ i = cyclogramResults.i;
+ rowNumber = cyclogramResults.rowNumber;
+ colNumber = cyclogramResults.colNumber;
+ }
+ }
+});