diff --git a/voicegardens/static/styles.css b/voicegardens/static/styles.css index b7422fa..d0b76c4 100644 --- a/voicegardens/static/styles.css +++ b/voicegardens/static/styles.css @@ -1,57 +1,56 @@ /* Custom CSS styles */ @font-face { -font-family: 'Signika Bold'; -src: url('fonts/Signika-Bold.ttf') format('ttf'); -font-weight: bold; -font-style: normal; + font-family: "Signika Bold"; + src: url("fonts/Signika-Bold.ttf") format("ttf"); + font-weight: bold; + font-style: normal; } @font-face { -font-family: 'Signika'; -src: url('fonts/Signika-Regular.ttf') format('ttf'); -font-weight: normal; -font-style: normal; + font-family: "Signika"; + src: url("fonts/Signika-Regular.ttf") format("ttf"); + font-weight: normal; + font-style: normal; } @font-face { -font-family: 'Rubik'; -src: url('fonts/Rubik-Bold.ttf') format('ttf'), - url('fonts/rubik-bold-webfont.woff') format('woff'), - url('fonts/rubik-bold-webfont.woff2') format('woff2'); -font-weight: bold; -font-style: normal; + font-family: "Rubik"; + src: url("fonts/Rubik-Bold.ttf") format("ttf"), + url("fonts/rubik-bold-webfont.woff") format("woff"), + url("fonts/rubik-bold-webfont.woff2") format("woff2"); + font-weight: bold; + font-style: normal; } @font-face { -font-family: 'Rubik'; -src: url('fonts/Rubik-Regular.ttf') format('ttf'); -font-weight: normal; -font-style: normal; + font-family: "Rubik"; + src: url("fonts/Rubik-Regular.ttf") format("ttf"); + font-weight: normal; + font-style: normal; } @font-face { -font-family: 'Rubik'; -src: url('fonts/Rubik-Italic.ttf') format('ttf'); -font-weight: normal; -font-style: italic; + font-family: "Rubik"; + src: url("fonts/Rubik-Italic.ttf") format("ttf"); + font-weight: normal; + font-style: italic; } @font-face { -font-family: 'PalanquinDark'; -src: url('fonts/PalanquinDark-Bold.ttf') format('ttf'); -font-weight: bold; -font-style: normal; + font-family: "PalanquinDark"; + src: url("fonts/PalanquinDark-Bold.ttf") format("ttf"); + font-weight: bold; + font-style: normal; } @font-face { -font-family: 'PalanquinDark'; -src: url('fonts/PalanquinDark-Regular.ttf') format('ttf'); -font-weight: normal; -font-style: normal; + font-family: "PalanquinDark"; + src: url("fonts/PalanquinDark-Regular.ttf") format("ttf"); + font-weight: normal; + font-style: normal; } - body { height: 100vh; margin: 0px; @@ -70,48 +69,48 @@ img.button { } .record { - position:fixed; - top:20px; - left:20px; + position: fixed; + top: 20px; + left: 20px; } .record:hover { - top:22px; - left:22px; + top: 22px; + left: 22px; } .stop { - position:fixed; - top:100px; - left:20px; + position: fixed; + top: 100px; + left: 20px; } .stop:hover { - top:102px; - left:22px; + top: 102px; + left: 22px; } .leaf { - position:fixed; - top:180px; - left:20px; + position: fixed; + top: 180px; + left: 20px; } .leaf:hover { - top:182px; - left:22px; + top: 182px; + left: 22px; } .about { width: 3% !important; - position:fixed; - top:260px; - left:20px; + position: fixed; + top: 260px; + left: 20px; } .about:hover { - top:262px; - left:22px; + top: 262px; + left: 22px; } .about-text { @@ -129,11 +128,11 @@ h1 { } .backtoindex img { - width: 10%; - margin: 0 auto !important; - margin-left: 20vw; - padding: 3em 2em 0 10em; - float: left; + width: 10%; + margin: 0 auto !important; + margin-left: 20vw; + padding: 3em 2em 0 10em; + float: left; } .button.button-about { @@ -161,15 +160,15 @@ div.footer { @media screen and (max-width: 770px) { img.button { width: 10%; -} + } .about { - width: 7% !important; - top: unset; - right: 25vw; - bottom: 2vh; - left: unset; - } + width: 7% !important; + top: unset; + right: 25vw; + bottom: 2vh; + left: unset; + } .about:hover { width: 7% !important; @@ -177,43 +176,42 @@ div.footer { right: 25vw; bottom: 2vh; left: unset; - - } + } .leaf { - top: unset; - bottom: 3vh; - left: 55vw; + top: unset; + bottom: 3vh; + left: 55vw; } .leaf:hover { - top: unset; - bottom: 3vh; - left: 55vw; + top: unset; + bottom: 3vh; + left: 55vw; } .stop { - top: unset; - bottom: 3vh; - left: 40vw; + top: unset; + bottom: 3vh; + left: 40vw; } .stop:hover { - top: unset; - bottom: 3vh; - left: 40vw; + top: unset; + bottom: 3vh; + left: 40vw; } .record { - top: unset; - bottom: 3vh; - left: 25vw; + top: unset; + bottom: 3vh; + left: 25vw; } .record:hover { - top: unset; - bottom: 3vh; - left: 25vw; + top: unset; + bottom: 3vh; + left: 25vw; } .about-text { diff --git a/voicegardens/static/voicegardens.js b/voicegardens/static/voicegardens.js index 33cccef..0b59074 100644 --- a/voicegardens/static/voicegardens.js +++ b/voicegardens/static/voicegardens.js @@ -55,11 +55,9 @@ var shapes = []; var amplitude; var duration; -// random shape positioning -var positionTick = false; -var secondTick = false; -var secondTimer = 0; -var positionTimer = 0; +// The background colour choices for the environment +var bgChoices; +var bgColour; function record() { /** @@ -201,7 +199,7 @@ class GeneratedShape { else this.soundAmplitude = soundAmplitude; // mouse hover awareness for sound playing - this.hover = false; + this.hovering = false; // The opacity of the shape. This controls whether we can see the shape or // not (transparency). It starts at zero as we want to fade the shapes in @@ -216,14 +214,8 @@ class GeneratedShape { this.accelX = 0.0; this.accelY = 0.0; - // The x, y destination values which the shape moves towards. This can be - // calculated against the `mouseX` and `mouseY` values, for example. Then - // the shape will follow the mouse. - this.deltaX = 0.0; - this.deltaY = 0.0; - // The speed at which the shape 'springs' towards its final destination. - this.springing = 0.0009; + this.springing = random(0.0006, 0.0009); // The speed at which the shape rocks back and forth when it is in the // process of coming to a halt. @@ -232,7 +224,7 @@ class GeneratedShape { // Value that controls the tightness or looseness of the curves between the // x,y values of the shape. AFAIK, this value can go between -5 and +5. // With +5 we have very sharp curves and edges. - this.organicConstant = random(-5, 5); + this.organicConstant = 1.0; // The x,y values which determine where the shape is currently. These are // required in order to calculate where the shape is currently so that we @@ -275,15 +267,24 @@ class GeneratedShape { this.angle = radians(360 / this.edges); // ??? - this.centerX = random(windowWidth); - this.centerY = random(windowHeight); + this.centerX = random(windowWidth) - toScreenX; + this.centerY = random(windowHeight) - toScreenY; // new destination for the shapes this.destX = random(windowWidth); this.destY = random(windowHeight); - // the shape that was last collided with - this.lastCollidedShape; + // The x, y destination values which the shape moves towards. This can be + // calculated against the `mouseX` and `mouseY` values, for example. Then + // the shape will follow the mouse. + this.deltaX = this.destX - this.centerX - toScreenX; + this.deltaY = this.destY - this.centerY - toScreenY; + + // time management for timing when to make new random position + // movements + this.drawTimer = 0; + this.nextTick = random(1000, 9000); + this.tickTimer = 0; this.initialise(); } @@ -302,8 +303,8 @@ class GeneratedShape { // this directly influences the shape of the size alongside the // this.radius shape value - this.randXs[i] = ceil(this.soundDuration * random(-30, 30)); - this.randYs[i] = ceil(this.soundDuration * random(-30, 30)); + this.randXs[i] = this.soundDuration * random(-10, 40); + this.randYs[i] = this.soundDuration * random(-10, 40); } for (let i = 0; i < this.edges; i++) { @@ -324,7 +325,7 @@ class GeneratedShape { for (let i = 0; i < shapes.length; i++) { let shape = shapes[i]; - if (this === shape || this.lastCollidedShape === shape) { + if (this === shape) { continue; } @@ -338,7 +339,6 @@ class GeneratedShape { ); if (collision === true) { - this.lastCollidedShape = shape; return [true, shape]; } } @@ -376,14 +376,37 @@ class GeneratedShape { /** * Choose a colour for the shape. **/ - // TODO: choose nicer colours - // TODO: Can we have gradient colours - // TODO: Can we have multiple colours let colourChoices = [ - color("red"), - color("blue"), - color("green"), - color("black") + color("#4F6EE8"), + color("#626788"), + color("#334171"), + color("#1529C2"), + color("#A17AA3"), + color("#606CEB"), + color("#8A77D5"), + color("#EB4913"), + color("#FC6012"), + color("#D94C14"), + color("#F08A60"), + color("#F8988F"), + color("#6E4F47"), + color("#93E35B"), + color("#DE3F16"), + color("#D1611F"), + color("#C22F0A"), + color("#C97814"), + color("#EDA714"), + color("#D5894A"), + color("#448F54"), + color("#61C26F"), + color("#ACE9B2"), + color("#CC25B6"), + color("#D695F0"), + color("#C5C2F0"), + color("#CC3D25"), + color("#A3614E"), + color("#F0DBA9"), + color("#7C4531") ]; let index = floor(random(0, colourChoices.length)); @@ -404,9 +427,27 @@ class GeneratedShape { for (let i = 0; i < this.edges; i++) { curveVertex(this.xs[i], this.ys[i]); } + for (let i = 0; i < this.edges - 1; i++) { + curveVertex(this.xs[i], this.ys[i]); + } endShape(CLOSE); } + tick() { + /** + * Manage internal time for each shape. + **/ + this.drawTimer = millis(); + + if (this.drawTimer >= this.nextTick + this.tickTimer) { + this.tickTimer = millis(); + this.nextTick = random(1000, 9000); + return true; + } + + return false; + } + draw() { /** * Draw the shape vectors. @@ -424,6 +465,26 @@ class GeneratedShape { this.curve(); } + hover() { + /** + * React to mouse hovering. + **/ + let isHovering = collidePointPoly( + mouseX - screenX, + mouseY - screenY, + this.vectors + ); + + if (isHovering === true) { + if (this.hovering === false) { + this.sound(); + this.hovering = true; + } + } else { + this.hovering = false; + } + } + move() { /** * Move the shape vectors. @@ -463,33 +524,31 @@ function setup() { createCanvas(windowWidth, windowHeight); frameRate(frameRate); setupRecording(); + + bgChoices = [ + color("#F6B2FF"), + color("#F58F6C"), + color("#C3EFDB"), + color("#ADCA95"), + color("#F58F6C"), + color("#A5F1F7"), + color("#FFC266"), + color("#FF66BB"), + color("#F6B2FF"), + color("#CFE4D9") + ]; + bgColour = bgChoices[floor(random(0, bgChoices.length - 1))]; } function draw() { /** * The draw loop which is called x times a second where x is the frameRate. **/ - background("white"); + background(bgColour); blendMode(BLEND); smooth(); noStroke(); - // count random waiting times in seconds until choosing a new destX, destY - // for a moving shape - let nextPositionTick = random(3000, 8000); - let positionTickingTime = millis(); - if (positionTickingTime >= nextPositionTick + positionTimer) { - positionTick = true; - positionTimer = millis(); - } - - let nextSecondTick = 1000; - let secondTickingTime = millis(); - if (secondTickingTime >= nextSecondTick + secondTimer) { - secondTick = true; - secondTimer = millis(); - } - // offset the window view based on new values of x,y related to the screen. // These values are generated once the user drags the screen with the mouse. screenX = lerp(screenX, toScreenX, 0.2); @@ -509,45 +568,22 @@ function draw() { for (let i = 0; i < shapes.length; i++) { let shape = shapes[i]; - // if hovering over the shape, play the recorded sound - let hovering = collidePointPoly( - mouseX - screenX, - mouseY - screenY, - shape.vectors - ); - if (hovering === true) { - if (shape.hover === false) { - shape.sound(); - shape.hover = true; - } - } else { - shape.hover = false; - } + shape.hover(); + shape.draw(); + shape.move(); - // randomly move the shapes - if (positionTick) { + if (shape.tick() === true) { shape.destX = random(windowWidth); shape.destY = random(windowHeight); - - // also reset last collided shape - shape.lastCollidedShape = undefined; } - shape.draw(); - shape.move(); - // play recordings when shapes collide let [collision, collidedShape] = shape.collide(shapes); if (collision === true) { - if (secondTick) { - shape.sound(); - collidedShape.sound(); - } + shape.sound(); + collidedShape.sound(); } } - - // reset random shape position time ticker - positionTick = false; } function mouseDragged() { diff --git a/voicegardens/templates/about.html b/voicegardens/templates/about.html index 8faf3ed..70ef96e 100644 --- a/voicegardens/templates/about.html +++ b/voicegardens/templates/about.html @@ -13,7 +13,7 @@

Kari Robertson
- Wet Signal Voice Gardens

+ Wet Signal Voice Gardens

1. Key

@@ -49,7 +49,7 @@

Kari also plays in the improvisational/experimental band Difficult with Eothan Stearn and Tracy Hanna.

4. How the page works

-

This website is made using mainly flask and p5js.

+

This website is made using mainly Flask and P5js.

Users are invited to record their voices speaking or making non-verbal sounds into the site which the software will then translate into visual forms. Audio attributes such as amplitude and duration of sound recordings are translated into the visual properties of size, number of edges, and colour. Users can play with using the different qualities of their voice to create varied and dynamic visualisations. The recordings are then ‘planted’ in the voicegarden (or added to the archive).

@@ -83,7 +83,7 @@

7. Licensing for the code

- The code for this website is licensed under AGPL and can be found here. + The code for this website is licensed under the AGPL and can be found here.

8. Browser Compatibility