Created a Synth class, updated page layouts and interactivity. todo: improve audio feedback, flesh out teach section, and find a system to theme site on.
This commit is contained in:
parent
ab4f7128c9
commit
16899b823a
|
@ -23,7 +23,7 @@ nav {
|
||||||
nav>span {
|
nav>span {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
color: #1e1e3c33;
|
color: #1e1e3c33;
|
||||||
transition: color ease 0.1s;
|
transition: color ease 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav>span:hover {
|
nav>span:hover {
|
||||||
|
@ -32,17 +32,27 @@ nav>span:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav>span.active {
|
||||||
|
color: #1e1e3cff;
|
||||||
|
transition: color 1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav>span.active::after {
|
||||||
|
color: #1e1e3cff;
|
||||||
|
margin-left: 14px;
|
||||||
|
content: attr(data-link);
|
||||||
|
}
|
||||||
|
|
||||||
h1#menuItem {
|
h1#menuItem {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 31vw;
|
font-size: 32vw;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(300px);
|
transform: translateY(300px);
|
||||||
transition: opacity ease-in-out 0.1s, transform ease-in-out 0.1s;
|
transition: opacity ease-in-out 0.1s, transform ease-in-out 0.1s;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1#menuItem.active {
|
h1#menuItem.active {
|
||||||
|
@ -52,6 +62,36 @@ h1#menuItem.active {
|
||||||
|
|
||||||
/* --> MAINPAGE CONSTRUCTION <-- */
|
/* --> MAINPAGE CONSTRUCTION <-- */
|
||||||
|
|
||||||
section#content {
|
section.content {
|
||||||
margin: 40px 20px;
|
display: grid;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.content.visible {
|
||||||
|
margin: 20px 67px;
|
||||||
|
opacity: 1;
|
||||||
|
height: inherit;
|
||||||
|
overflow: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.content ul.subMenu {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.content ul.subMenu li {
|
||||||
|
border-left: thin solid #d2d2d8;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.content ul.subMenu li:hover {
|
||||||
|
background: #d2d2d8;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
70
index.html
70
index.html
|
@ -8,18 +8,20 @@
|
||||||
<link rel="stylesheet" type="text/css" href="css/resets.css">
|
<link rel="stylesheet" type="text/css" href="css/resets.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/master.css">
|
<link rel="stylesheet" type="text/css" href="css/master.css">
|
||||||
|
|
||||||
<script src="js/interaction.js" charset="utf-8" defer></script>
|
<script src="js/synth.js" charset="utf-8" defer></script>
|
||||||
<script src="js/audio.js" charset="utf-8" defer></script>
|
<script src="js/audio.js" charset="utf-8" defer></script>
|
||||||
|
<script src="js/interaction.js" charset="utf-8" defer></script>
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<span data-link="works">█</span>
|
<span data-link="~"><~</span>
|
||||||
<span data-link="audio">█</span>
|
<span data-link="works" class="">█</span>
|
||||||
<span data-link="teach">█</span>
|
<span data-link="audio" class="">█</span>
|
||||||
|
<span data-link="teach" class="">█</span>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<section style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: -100;">
|
<section style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: -100;">
|
||||||
|
@ -28,7 +30,63 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="content">
|
|
||||||
|
<section id="works" class="content">
|
||||||
|
|
||||||
|
<ul class="subMenu">
|
||||||
|
<li data-link="fragile">Fragile Mastery</li>
|
||||||
|
<li data-link="euclid">EUCLID</li>
|
||||||
|
<li data-link="autoArchive">Autonomous Archive</li>
|
||||||
|
<li data-link="adelaide">Adelaïde</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="audio" class="content">
|
||||||
|
|
||||||
|
<ul class="subMenu">
|
||||||
|
<li data-link="solo">solo</li>
|
||||||
|
<li data-link="postoast">postoast</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<section class="content">
|
||||||
|
<div class="imgFeat">
|
||||||
|
<img src="../assets/audio/solo/thumb-_DSC5695.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="https://work.suroh.tk/" id="home"><< home</a>
|
||||||
|
|
||||||
|
<section class="work text">
|
||||||
|
<h1>Solo</h1>
|
||||||
|
|
||||||
|
<p>My solo practice embraces the unstable nature of improvisation in software and in music. I aim to examine the agency that improvisation can provide in the context of creative software and performance. I build custom software that acts both as an instrument and collaborator which I perform alongside with a combination of digital hardware and traditional musical instruments. Through the employment of these tools I challenge my ingrained jazz improvisational training, and examine creative relationships we can have with machines.</p>
|
||||||
|
|
||||||
|
<iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/471930894&color=%23333333&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true"></iframe>
|
||||||
|
|
||||||
|
<p>My improvisations to date have included hacking MIDI controllers and playing with audio analysis. Such approaches deconstruct my traditional musical training on the saxophone as I find myself attempting to embody routines as defined by software. It is either the programming of my years of formal music training, or a conceived imposition by the "perfection" of technology that makes me question my agency, and even my role as an interactor.</p>
|
||||||
|
|
||||||
|
<p>My performance practice is coupled with research that takes many other forms, where I engage in discussion around agencies a wider adoption of improvisation could provide. Building these instruments is an extension of my improvisational method, which acts as a form of liberation against the standardisation of both musical practices and software development. By employing improvisation towards the development of my tools, I expose the agencies that it might afford us in realms outside of creative practices, and how they might be used to combat outdated structures of hierarchy and value.</p>
|
||||||
|
|
||||||
|
<p>All of the code is freely accessible on my git repository here : <a href="https://git.suroh.tk">git.suroh.tk</a>.</p>
|
||||||
|
|
||||||
|
<iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/476223060&color=%23333333&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false&show_teaser=true"></iframe>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="work imgs">
|
||||||
|
<img src="../assets/audio/solo/patch.png">
|
||||||
|
|
||||||
|
<img src="../assets/audio/solo/thumb-_DSC5654.png">
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="teach" class="content">
|
||||||
|
|
||||||
|
<p>teach content</p>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
|
||||||
|
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
45
js/audio.js
45
js/audio.js
|
@ -1,52 +1,11 @@
|
||||||
// audio for events
|
// audio for events
|
||||||
// AUDIO SETUP
|
// AUDIO SETUP
|
||||||
let audioEngine = new(window.AudioContext || window.webkitAudioContext)()
|
const audioEngine = new(window.AudioContext || window.webkitAudioContext)()
|
||||||
let masterGain = audioEngine.createGain()
|
const masterGain = audioEngine.createGain()
|
||||||
|
|
||||||
let voice = {
|
|
||||||
gain: audioEngine.createGain(),
|
|
||||||
osc: audioEngine.createOscillator(),
|
|
||||||
adsr: {
|
|
||||||
a: typeof amount === 'number' ? amount * 1000 : 0.025,
|
|
||||||
d: typeof amount === 'number' ? amount * 1000 : 0.5,
|
|
||||||
sv: typeof amount === 'number' ? amount : 0.8,
|
|
||||||
r: typeof amount === 'number' ? amount * 1000 : 0.5,
|
|
||||||
t: audioEngine.currentTime
|
|
||||||
},
|
|
||||||
play: function(note) {
|
|
||||||
freq = typeof note === 'number' ? note : (Math.random() * 350) + 50
|
|
||||||
this.osc.frequency.setValueAtTime(freq, audioEngine.currentTime)
|
|
||||||
this.engage()
|
|
||||||
},
|
|
||||||
engage: function() {
|
|
||||||
|
|
||||||
// clear preScheduled events
|
|
||||||
this.gain.gain.cancelScheduledValues(this.adsr.t)
|
|
||||||
// initial value
|
|
||||||
this.gain.gain.setValueAtTime(0.0, this.adsr.t)
|
|
||||||
// attack
|
|
||||||
this.gain.gain.setTargetAtTime(0.9, this.adsr.t, this.adsr.a)
|
|
||||||
// decay
|
|
||||||
this.gain.gain.setTargetAtTime(this.adsr.sv, this.adsr.t + this.adsr.a, this.adsr.d)
|
|
||||||
},
|
|
||||||
release: function() {
|
|
||||||
this.adsr.t = audioEngine.currentTime
|
|
||||||
// release
|
|
||||||
this.gain.gain.setTargetAtTime(0.0, this.adsr.t, this.adsr.r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
// create gain
|
// create gain
|
||||||
voice.gain.gain.setValueAtTime(0.0001, audioEngine.currentTime)
|
|
||||||
masterGain.gain.value = 0.9
|
masterGain.gain.value = 0.9
|
||||||
voice.gain.connect(masterGain)
|
|
||||||
|
|
||||||
// create oscilator
|
|
||||||
voice.osc.type = 'sine'
|
|
||||||
voice.osc.connect(voice.gain)
|
|
||||||
voice.osc.start()
|
|
||||||
|
|
||||||
masterGain.connect(audioEngine.destination)
|
masterGain.connect(audioEngine.destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,76 @@
|
||||||
|
|
||||||
const primaryNav = document.querySelector('nav')
|
const primaryNav = document.querySelector('nav')
|
||||||
const menuItem = document.querySelector('#menuItem')
|
const menuItem = document.querySelector('#menuItem')
|
||||||
|
const content = document.querySelectorAll('.content')
|
||||||
|
|
||||||
// TODO: (create voice class per button)
|
// TODO: (create voice class per button)
|
||||||
// let menuVoices = []
|
let menuVoices = []
|
||||||
|
|
||||||
|
const subMenu = (activePage) => {
|
||||||
|
let menu = document.querySelector(`\#${activePage} \.subMenu`)
|
||||||
|
if (menu) {
|
||||||
|
return menu
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showContent = (section) => {
|
||||||
|
for (let sect of content) {
|
||||||
|
if (c.dataset.link == sect.id) {
|
||||||
|
sect.className = 'content visible'
|
||||||
|
} else if (!c.dataset.link) {
|
||||||
|
sect.className = 'content'
|
||||||
|
} else {
|
||||||
|
sect.className = 'content'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let c of primaryNav.children) {
|
for (let c of primaryNav.children) {
|
||||||
// TODO: (create voice class per button)
|
// TODO: (create voice class per button)
|
||||||
// menuVoices[c.dataset.link] = new voice()
|
menuVoices[c.dataset.link] = new Synth(audioEngine)
|
||||||
|
menuVoices[c.dataset.link].gain.connect(masterGain)
|
||||||
|
|
||||||
c.addEventListener('click', () => {
|
c.addEventListener('click', () => {
|
||||||
console.log(`clicked : ${c.dataset.link}`)
|
menuVoices[c.dataset.link].mod()
|
||||||
|
|
||||||
|
for (let _c of primaryNav.children) {
|
||||||
|
if (c === _c && c.dataset.link != '~') {
|
||||||
|
c.className = 'active'
|
||||||
|
|
||||||
|
if (subMenu(c.dataset.link)) {
|
||||||
|
for (let s of subMenu(c.dataset.link).children) {
|
||||||
|
s.addEventListener('click', () => {
|
||||||
|
console.log(`clicked : ${s.dataset.link}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_c.className = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let sect of content) {
|
||||||
|
if (c.dataset.link == sect.id) {
|
||||||
|
sect.className = 'content visible'
|
||||||
|
} else if (!c.dataset.link) {
|
||||||
|
sect.className = 'content'
|
||||||
|
} else {
|
||||||
|
sect.className = 'content'
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
c.addEventListener('mouseenter', () => {
|
c.addEventListener('mouseenter', () => {
|
||||||
menuItem.innerHTML = c.dataset.link
|
menuItem.innerHTML = c.dataset.link
|
||||||
menuItem.className = 'active'
|
menuItem.className = 'active'
|
||||||
voice.play()
|
menuVoices[c.dataset.link].noteOn()
|
||||||
})
|
})
|
||||||
|
|
||||||
c.addEventListener('mouseout', () => {
|
c.addEventListener('mouseout', () => {
|
||||||
menuItem.className = 'inactive'
|
menuItem.className = 'inactive'
|
||||||
voice.release()
|
menuVoices[c.dataset.link].noteOff()
|
||||||
})
|
})
|
||||||
|
|
||||||
menuItem.addEventListener('transitionend', () => {
|
menuItem.addEventListener('transitionend', () => {
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
class Synth {
|
||||||
|
// SYNTH VOICE CONSTRUCTOR
|
||||||
|
// Variables?
|
||||||
|
constructor(audio, type, env = {
|
||||||
|
a: 0.2,
|
||||||
|
r: 0.5,
|
||||||
|
}) {
|
||||||
|
|
||||||
|
this.audio = audio
|
||||||
|
this.t = this.audio.currentTime
|
||||||
|
|
||||||
|
this.env = {
|
||||||
|
a: env.a,
|
||||||
|
r: env.r,
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRIMARY GAIN
|
||||||
|
this.gain = this.audio.createGain()
|
||||||
|
this.gain.gain.value = 0
|
||||||
|
|
||||||
|
// PRIMARY VOICE
|
||||||
|
this.primVoice = this.audio.createOscillator()
|
||||||
|
this.primVoice.type = typeof type === 'string' ? type : 'sine'
|
||||||
|
this.primVoice.connect(this.gain)
|
||||||
|
this.primVoice.start()
|
||||||
|
|
||||||
|
// MOD GAIN
|
||||||
|
this.modGain = this.audio.createGain()
|
||||||
|
this.modGain.gain.value = 0
|
||||||
|
this.modGain.connect(this.gain)
|
||||||
|
|
||||||
|
// MOD VOICE
|
||||||
|
this.modVoice = this.audio.createOscillator()
|
||||||
|
this.modVoice.type = 'triangle'
|
||||||
|
this.modVoice.connect(this.modGain)
|
||||||
|
this.modVoice.start()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// OSC -> ADSR -> GAIN
|
||||||
|
noteOn(freq) {
|
||||||
|
// note frequency
|
||||||
|
this.freq = freq ? freq : (Math.random() * 350) + 50
|
||||||
|
this.primVoice.frequency.setValueAtTime(this.freq, this.t)
|
||||||
|
this.modVoice.frequency.setValueAtTime(this.freq - 20, this.t)
|
||||||
|
// clear preScheduled events
|
||||||
|
this.gain.gain.cancelScheduledValues(this.audio.currentTime)
|
||||||
|
// attack
|
||||||
|
this.gain.gain.setTargetAtTime(0.9, this.t + this.env.a, 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
noteOff() {
|
||||||
|
// release
|
||||||
|
this.gain.gain.setTargetAtTime(0.0, this.t + this.env.r, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod() {
|
||||||
|
this.modGain.gain.cancelScheduledValues(this.audio.currentTime)
|
||||||
|
// attack
|
||||||
|
this.modGain.gain.setTargetAtTime(0.9, this.audio.currentTime, 0.5)
|
||||||
|
// release
|
||||||
|
this.modGain.gain.setTargetAtTime(0.0, this.audio.currentTime + 0.4, 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="../css/styles.css">
|
<link rel="stylesheet" type="text/css" href="../css/master.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="../css/styles.css">
|
<link rel="stylesheet" type="text/css" href="../css/master.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="../css/styles.css">
|
<link rel="stylesheet" type="text/css" href="../css/master.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="../css/styles.css">
|
<link rel="stylesheet" type="text/css" href="../css/master.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="../css/styles.css">
|
<link rel="stylesheet" type="text/css" href="../css/master.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="../css/styles.css">
|
<link rel="stylesheet" type="text/css" href="../css/master.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue