Skip to content

Commit

Permalink
Add a /sounds demo page with new vanilla sounds
Browse files Browse the repository at this point in the history
  • Loading branch information
oskarrough committed Jan 22, 2024
1 parent 45b719e commit 63281dc
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 43 deletions.
77 changes: 77 additions & 0 deletions src/ui/pages/sounds.astro
@@ -0,0 +1,77 @@
---
import Layout from '../layouts/Layout.astro'
import '../styles/index.css'
const sounds = ['startGame', 'startTurn', 'endTurn', 'selectCard', 'cardToHand', 'playCard']
---

<script>
import sounds from '../sounds.js'
import {beep, playCoin, playWhoosh} from '../sounds2.js'

const buttons = document.querySelectorAll('menu.sounds button')
buttons.forEach((button) => {
button.addEventListener('click', (event) => {
const sound = event.currentTarget.dataset.sound
console.log('playing', sound)
sounds[sound]()
})
})

document.querySelectorAll('[data-sfx]').forEach((el) => {
el.addEventListener('click', () => {
const {sfx, coin} = el.dataset
if (sfx === 'whoosh') {
playWhoosh(0.6)
} else if (coin) {
playCoin(coin)
} else {
beep(sfx, sfx * 2, 0.3)
}
})
})
</script>

<Layout title="Slay the Web">
<div class="Container">
<h1>Sounds</h1>
<p>Play any of the sound effects used in the game.</p>

<menu>
<button type="button" data-sfx="220">220</button>
<button type="button" data-sfx="440">440</button>
<button type="button" data-sfx="880">880</button>
<button type="button" data-sfx data-coin="minor">Coin minor</button>
<button type="button" data-sfx data-coin="major">Coin major</button>
<button type="button" data-sfx data-coin="pentatonic">Coin penta</button>
<button type="button" data-sfx="whoosh">Whoosh</button>
</menu>

<menu class="sounds">
{
sounds.map((s) => (
<button type="button" data-sound={s}>
{s}
</button>
))
}
</menu>
</div>
</Layout>

<style>
h1 + p {
margin-left: 0.8em;
margin-bottom: 2rem;
}
menu {
margin: 0 0 1rem;
display: flex;
flex-flow: column wrap;
gap: 0.5em;
max-width: 14em;
}
button {
margin: 0;
}
</style>
86 changes: 43 additions & 43 deletions src/ui/sounds.js
Expand Up @@ -14,52 +14,46 @@ function startGame() {
polySynth.triggerAttackRelease(['D4', 'F4', 'A4', 'C5', 'E5'], 0.7)
}

const selectCard = () => {
// initialize the noise and start
const noise = new Tone.Noise({
type: 'brown',
fadeOut: 0.07,
volume: -33,
}).start()
const brownNoise = new Tone.Noise({
type: 'brown',
fadeOut: 0.07,
volume: -33,
})

const autoFilter = new Tone.AutoFilter({
frequency: '5n',
baseFrequency: 3000,
octaves: 2,
}).toDestination()

const selectCard = () => {
brownNoise.start()
// make an autofilter to shape the noise
const autoFilter = new Tone.AutoFilter({
frequency: '5n',
baseFrequency: 3000,
octaves: 2,
})
.toDestination()
.start()
autoFilter.stop('+0.1')

// connect the noise
noise.connect(autoFilter)
autoFilter.start().stop('+0.1')
brownNoise.connect(autoFilter)
// start the autofilter LFO
noise.stop('+0.04')
brownNoise.stop('+0.04')
}

const pinkNoise = new Tone.Noise({
type: 'pink',
fadeOut: 0.2,
volume: -28,
})

function endTurn() {
// initialize the noise and start
const noise = new Tone.Noise({
type: 'pink',
fadeOut: 0.2,
volume: -28,
}).start()
pinkNoise.start()

// make an autofilter to shape the noise
const autoFilter = new Tone.AutoFilter({
frequency: '2n',
baseFrequency: 200,
octaves: 2,
})
.toDestination()
.start()
autoFilter.stop('+0.4')
.stop('+0.4')

// connect the noise
noise.connect(autoFilter)
// start the autofilter LFO
noise.stop('+0.2')
pinkNoise.connect(autoFilter).stop('+0.2')
}

function startTurn() {
Expand Down Expand Up @@ -104,22 +98,28 @@ function cardToHand() {
})
.toDestination()
.start()
autoFilter.stop('+0.08')
.stop('+0.08')

// connect the noise
noise.connect(autoFilter)
// start the autofilter LFO
noise.stop('+0.1')
noise
.connect(autoFilter)
// start the autofilter LFO
.stop('+0.1')
}

function playCard(card) {
// const cardType = card.damage ? 'attack' : 'defense'
// if (cardType === 'attack') {
// playAttackCard()
// }
// if (cardType === 'defense') {
// playDefenseCard()
// }
playGenericCard()
}

function playCard({card}) {
const cardType = card.damage ? 'attack' : 'defense'
if (cardType === 'attack') {
playAttackCard()
}
if (cardType === 'defense') {
playDefenseCard()
}
const playGenericCard = () => {
amSynth.triggerAttackRelease('G#3', 0.1)
}

const playAttackCard = () => {
Expand Down
75 changes: 75 additions & 0 deletions src/ui/sounds2.js
@@ -0,0 +1,75 @@
const audioContext = new (window.AudioContext || window.webkitAudioContext)()

/**
* Plays a beep sound using web audio api
* AudioContext > Oscillator -> Gain
*/
export function beep(
startFrequency = 440,
endFrequency = 880,
duration = 0.5,
waveform = 'square',
volume = 0.1,
) {
const oscillator = audioContext.createOscillator()
oscillator.type = waveform // 'square', 'sawtooth', 'triangle', or 'sine'
oscillator.frequency.setValueAtTime(startFrequency, audioContext.currentTime)
if (endFrequency && duration > 0) {
oscillator.frequency.exponentialRampToValueAtTime(endFrequency, audioContext.currentTime + duration)
}

// Adding a simple gain envelope for a more "blippy" sound
const gainNode = audioContext.createGain()
gainNode.gain.setValueAtTime(0, audioContext.currentTime)
gainNode.gain.linearRampToValueAtTime(volume, audioContext.currentTime + 0.01) // Quick ramp up
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration) // And down

oscillator.connect(gainNode)
gainNode.connect(audioContext.destination)

oscillator.start()
oscillator.stop(audioContext.currentTime + duration)
}

const majorScale = [523.25, 587.33, 659.25, 698.46, 783.99, 880.0, 987.77, 1046.5] // C5, D5, E5, F5, G5, A5, B5, C6
const minorScale = [440.0, 523.25, 587.33, 659.25, 698.46, 783.99, 880.0, 987.77] // A4, C5, D5, E5, F5, G5, A5, B5
const pentatonicScale = [523.25, 659.25, 783.99, 880.0, 1046.5] // C5, E5, G5, A5, C6

/**
* Plays a coin/reward high pitched sound
*/
export function playCoin(type, volume = 0.5) {
const scales = [majorScale, minorScale, pentatonicScale]
let selectedScale = scales[Math.floor(Math.random() * scales.length)]
if (type === 'major') selectedScale = majorScale
if (type === 'minor') selectedScale = minorScale
if (type === 'pentatonic') selectedScale = pentatonicScale

selectedScale.forEach((note, index) => {
setTimeout(() => {
beep(note, note, 0.1, 'triangle', volume)
}, index * 100)
})
}

export function playWhoosh(duration = 1, startFreq = 200, endFreq = 800, volume = 0.2) {
const audioContext = new (window.AudioContext || window.webkitAudioContext)()

// Sine wave oscillator
const oscillator = audioContext.createOscillator()
oscillator.type = 'sine'
oscillator.frequency.setValueAtTime(startFreq, audioContext.currentTime)
oscillator.frequency.exponentialRampToValueAtTime(endFreq, audioContext.currentTime + duration)

// Gain envelope for smooth fade-in and fade-out
const gainNode = audioContext.createGain()
gainNode.gain.setValueAtTime(0, audioContext.currentTime)
gainNode.gain.linearRampToValueAtTime(volume, audioContext.currentTime + duration * 0.2)
gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + duration)

oscillator.connect(gainNode)
gainNode.connect(audioContext.destination)

oscillator.start()
oscillator.stop(audioContext.currentTime + duration)
}

0 comments on commit 63281dc

Please sign in to comment.