diff --git a/convulse.css b/convulse.css
index 8b3dd1d..3bf59dc 100644
--- a/convulse.css
+++ b/convulse.css
@@ -4,13 +4,8 @@ body {
margin: 0;
font-family: sans-serif;
}
-video {
- height: 100vh;
- width: 100vw;
-}
canvas {
width: 100vw;
- filter: grayscale(100%);
}
canvas.recording {
filter: initial;
@@ -27,22 +22,39 @@ canvas.recording {
position: fixed;
z-index: 2;
top: 0;
- background-color: blue;
+ background-color: rgba(40, 40, 200, 0.3);
width: 100%;
- opacity: 0.3;
text-align: center;
}
#controls {
position: fixed;
z-index: 2;
width: 100%;
- background-color: ;
+ background-color: rgba(0, 0, 0, 0.8);
opacity: 0.8;
bottom: 0;
}
-video {
- display: none;
+#rec, #save {
+ font: 12pt sans-serif;
+ padding: 1em 2em;
+ margin: 0.2em 1em;
+ background: #ccc;
+ border: none;
+ border-radius: 3px;
}
dt {
font-weight: bold;
+}
+#indicator {
+ position: fixed;
+ top: 0.3em;
+ right: 0.3em;
+ font: 18pt sans-serif;
+ color: red;
+ animation: blinker 0.5s cubic-bezier(0, 0.87, 0.58, 1) infinite alternate;
+}
+
+@keyframes blinker {
+ from { opacity: 0; }
+ to { opacity: 1; }
}
\ No newline at end of file
diff --git a/convulse.html b/convulse.html
index ca5e89f..ce4e5ed 100644
--- a/convulse.html
+++ b/convulse.html
@@ -24,6 +24,7 @@
+
⬤
@@ -31,17 +32,21 @@
diff --git a/convulse.js b/convulse.js
index abb66d2..7b98487 100644
--- a/convulse.js
+++ b/convulse.js
@@ -1,5 +1,7 @@
// jshint asi:true
+Math.TAU = Math.PI * 2
+
function toast(text, timeout=8000) {
let toasts = document.querySelector("#toasts")
if (! text) {
@@ -17,85 +19,41 @@ function toast(text, timeout=8000) {
class Convulse {
constructor() {
+ this.chunks = []
this.canvas = document.querySelector("canvas")
this.ctx = this.canvas.getContext("2d")
this.dllink = document.querySelector("#download")
this.webcamVideo = document.querySelector("#webcam")
this.desktopVideo = document.querySelector("#desktop")
- document.addEventListener("click", event => this.toggle())
- this.chunks = []
-
- document.addEventListener("mouseenter", e => this.showControls(true))
- document.addEventListener("mouseleave", e => this.showControls(false))
- this.init()
- }
-
- showControls(show) {
- let controls = document.querySelector("#controls")
- if (show) {
- controls.classList.remove("hidden")
- } else {
- controls.classList.add("hidden")
- }
- }
-
- download(event) {
- let recording = window.URL.createObjectURL(new Blob(this.chunks, {type: this.recorder.mimeType}))
- let now = new Date().toISOString()
-
- this.dllink.addEventListener('progress', event => console.log(event))
- this.dllink.href = recording
- this.dllink.download = "convulse-" + now + ".webm"
- this.dllink.click()
- }
-
- start() {
- toast(null)
- this.chunks = []
- this.recorder.start(10)
- this.canvas.classList.add("recording")
- }
-
- stop() {
- toast("stopped and downloaded")
- this.recorder.stop()
- this.canvas.classList.remove("recording")
- }
-
- toggle() {
- if (this.recorder.state == "recording") {
- this.stop()
- this.download()
- } else {
- this.start()
- }
- }
-
- frame(timestamp) {
- if (this.webcamVideo.videoWidth > 0) {
- let webcamAR = this.webcamVideo.videoWidth / this.webcamVideo.videoHeight
- let desktopAR = this.desktopVideo.videoWidth / this.desktopVideo.videoHeight
-
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
- this.ctx.drawImage(this.desktopVideo, 0, 0, this.canvas.height * desktopAR, this.canvas.height)
- this.ctx.drawImage(this.webcamVideo, 0, 0, webcamAR * 400, 400)
- if (timestamp % 2000 < 1000) {
- this.ctx.beginPath()
- this.ctx.strokeStyle = "red"
- this.ctx.lineWidth = "6"
- this.ctx.rect(20, 20, 150, 100)
- this.ctx.stroke()
- }
- }
-
- requestAnimationFrame(ts => this.frame(ts))
- }
-
- async init() {
this.canvas.width = 1920
this.canvas.height = 1080
+ document.querySelector("#indicator").classList.add("hidden")
+
+ document.addEventListener("mouseenter", e => this.showControls(true))
+ document.addEventListener("mouseleave", e => this.showControls(false))
+ document.querySelector("canvas").addEventListener("click", e => this.rec(e))
+ document.querySelector("#rec").addEventListener("click", e => this.rec(e))
+ //document.querySelector("#save").addEventListener("click", e => this.save(e))
+
+ document.querySelector("#webcam-size").addEventListener("input", e => this.setWebcamSize(e))
+ document.querySelector("#webcam-size").value = localStorage.webcamSize || 0.3
+ document.querySelector("#webcam-size").dispatchEvent(new Event("input"))
+ document.querySelector("#webcam-pos").addEventListener("click", e => this.setWebcamPos(e))
+ this.webcamPos = localStorage.webcamPos || 2
+
+ document.querySelector("#desktop-size").addEventListener("input", e => this.setDesktopSize(e))
+ document.querySelector("#desktop-size").value = localStorage.desktopSize || 2.0
+ document.querySelector("#desktop-size").dispatchEvent(new Event("input"))
+ document.querySelector("#desktop-pos").addEventListener("click", e => this.setDesktopPos(e))
+ this.desktopPos = localStorage.desktopPos || 0
+
+ this.init()
+ }
+
+ async init() {
+ this.webcamVideo.muted = true
this.webcamVideo.srcObject = await navigator.mediaDevices.getUserMedia({video: true, audio: true})
this.webcamVideo.play()
@@ -115,7 +73,7 @@ class Convulse {
console.log("Adding audio track", at)
}
- this.recorder = new MediaRecorder(this.canvas.captureStream(30), {mimeType: "video/webm"})
+ this.recorder = new MediaRecorder(this.mediaStream, {mimeType: "video/webm"})
this.recorder.addEventListener("dataavailable", event => {
if (event.data && event.data.size > 0) {
this.chunks.push(event.data)
@@ -126,6 +84,97 @@ class Convulse {
toast("Click anywhere to start and stop recording")
}
+
+ setWebcamSize(event) {
+ this.webcamSize = event.target.value
+ localStorage.webcamSize = this.webcamSize
+ }
+
+ setWebcamPos(event) {
+ this.webcamPos = (this.webcamPos + 1) % 9
+ localStorage.webcamPos = this.webcamPos
+ }
+
+ setDesktopSize(event) {
+ this.desktopSize = event.target.value
+ localStorage.desktopSize = this.desktopSize
+ }
+
+ setDesktopPos(event) {
+ this.desktopPos = (this.desktopPos + 1) % 9
+ localStorage.desktopPos = this.desktopPos
+ }
+
+ rec(event) {
+ let button = document.querySelector("#rec")
+ if (this.recorder.state == "recording") {
+ // Stop
+ this.recorder.stop()
+ this.canvas.classList.remove("recording")
+ document.querySelector("#indicator").classList.add("hidden")
+ button.textContent = "⏺️"
+ toast("Stopped")
+ this.save()
+ } else {
+ // Start
+ this.recorder.start(10)
+ this.canvas.classList.add("recording")
+ document.querySelector("#indicator").classList.remove("hidden")
+ button.textContent = "⏹️"
+ toast("Recording: click anywhere to stop")
+ }
+ }
+
+ showControls(show) {
+ let controls = document.querySelector("#controls")
+ if (show) {
+ controls.classList.remove("hidden")
+ } else {
+ controls.classList.add("hidden")
+ }
+ }
+
+ save(event) {
+ let recording = window.URL.createObjectURL(new Blob(this.chunks, {type: this.recorder.mimeType}))
+ let now = new Date().toISOString()
+ let saveButton = document.querySelector("#save")
+
+ saveButton.addEventListener('progress', event => console.log(event))
+ saveButton.href = recording
+ saveButton.download = "convulse-" + now + ".webm"
+ saveButton.click()
+ }
+
+ frame(timestamp) {
+ if (this.webcamVideo.videoWidth > 0) {
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
+
+ let desktopAR = this.desktopVideo.videoWidth / this.desktopVideo.videoHeight
+ let desktopHeight = this.canvas.height * this.desktopSize
+ let desktopWidth = desktopHeight * desktopAR
+ let desktopY = (this.canvas.height - desktopHeight) * (Math.floor(this.desktopPos / 3) / 2)
+ let desktopX = (this.canvas.width - desktopWidth) * (Math.floor(this.desktopPos % 3) / 2)
+ this.ctx.drawImage(
+ this.desktopVideo,
+ desktopX, desktopY,
+ desktopWidth, desktopHeight
+ )
+
+ let webcamAR = this.webcamVideo.videoWidth / this.webcamVideo.videoHeight
+ let webcamHeight = this.canvas.height * this.webcamSize
+ let webcamWidth = webcamHeight * webcamAR
+ let webcamY = (this.canvas.height - webcamHeight) * (Math.floor(this.webcamPos / 3) / 2)
+ let webcamX = (this.canvas.width - webcamWidth) * (Math.floor(this.webcamPos % 3) / 2)
+ this.ctx.drawImage(
+ this.webcamVideo,
+ webcamX, webcamY,
+ webcamWidth, webcamHeight
+ )
+ }
+
+ requestAnimationFrame(ts => this.frame(ts))
+ }
+
}
function init() {