--- /dev/null
+// App Config
+
+const kEntrypoint = "breadsticks";
+const kGap = 50;
+const kDrawDelay = 100;
+const kDarkDrawingColor = '#fff';
+const kLightDrawingColor = '#000';
+
+/***********************************************
+ * Breadstick generates a matrix of points
+ * and assigns a vector to each entry
+ * and then draws them
+ ***********************************************/
+
+/*
+ * Generate field of points in which to draw
+ */
+
+// Updates the field object using a canvas
+
+const updateFieldFromCanvas = (field, canvas) => {
+ updateFieldFromRect(field, canvas.getBoundingClientRect());
+
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ updateFieldFromRect(field, canvas.getBoundingClientRect());
+ });
+};
+
+// Updates a field object using a rect
+
+const updateFieldFromRect = (field, rect) => {
+ field.width = rect.width;
+ field.height = rect.height;
+}
+
+/*
+ * Generate the matrix of vectors
+ */
+
+// Updates a matrix for a given field using the mouse
+
+
+const updateFieldMatrixFromMouse = (field, matrix, gap = 50) => {
+ const mousePosition = {
+ x: 0,
+ y: 0
+ };
+
+ fillMatrix(matrix, mousePosition.x, mousePosition.y, field.width, field.height, gap);
+
+ const updateMousePosition = document.addEventListener('mousemove', (event) => {
+ mousePosition.x = event.offsetX;
+ mousePosition.y = event.offsetY;
+ fillMatrix(matrix, mousePosition.x, mousePosition.y, field.width, field.height, gap);
+ });
+};
+
+// Gets a matrix by calculating the offset between position and field.
+
+const fillMatrix = (matrix, x, y, w, h, gap = 50) => {
+ for (let i = 0; i < w / gap; ++i) {
+ matrix[i] = [];
+ for (let j = 0; j < h / gap; ++j) {
+ let targetX = i * gap;
+ let targetY = j * gap;
+ matrix[i][j] = calculateOffsetVector(x, y, targetX, targetY, w, h);
+ }
+ }
+};
+
+// Calculates the offset vector of a point
+const calculateOffsetVector = (sourceX, sourceY, targetX, targetY, w, h) => {
+ const xOffset = targetX - sourceX;
+ const yOffset = targetY - sourceY;
+
+ return {
+ position: {
+ x: targetX,
+ y: targetY
+ },
+ angle: Math.atan2(xOffset, yOffset),
+ magnitude: Math.sqrt(Math.pow(xOffset, 2) + Math.pow(yOffset, 2))
+ };
+};
+
+
+/*
+ * Drawing
+ */
+
+// draw a vector from center
+const drawVector = (context, i, j, vector) => {
+ const origin = {
+ x: context.canvas.width / 2,
+ y: context.canvas.height / 2
+ };
+ const x = origin.x - vector.magnitude * Math.cos(vector.angle);
+ const y = origin.y - vector.magnitude * Math.sin(vector.angle);
+ context.beginPath();
+ context.moveTo(vector.position.x, vector.position.y);
+ context.lineTo(Math.round(x), Math.round(y));
+ context.stroke();
+};
+
+// Gets the color depending on the team
+
+const getColor = () => {
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+ return kDarkDrawingColor;
+ }
+
+ return kLightDrawingColor;
+};
+
+
+// Draws field on canvas
+
+const drawFieldMatrixOnCanvas = (matrix, context) => {
+
+ context.strokeStyle = getColor();
+ context.clearRect(0, 0, context.canvas.width, context.canvas.height);
+
+ for (let i = 0; i < matrix.length; ++i) {
+ const row = matrix[i];
+ for (let j = 0; j < row.length; ++j) {
+ const vector = row[j]
+
+ if (vector) {
+ drawVector(context, i, j, vector);
+ }
+ }
+ }
+};
+
+/*
+ * Entrypoint
+ */
+
+const run = () => {
+
+ const field = {
+ width: 0,
+ height: 0
+ };
+ const canvas = document.getElementById(kEntrypoint);
+
+ updateFieldFromCanvas(field, canvas);
+
+ const matrix = [[]];
+ updateFieldMatrixFromMouse(field, matrix, kGap);
+
+ const context = canvas.getContext('2d');
+ const drawFunction = () => {
+ drawFieldMatrixOnCanvas(matrix, context);
+ setTimeout(drawFunction, kDrawDelay);
+ }
+
+ setTimeout(drawFunction, 0);
+};
+
+window.addEventListener('load', run);