@@ -12,6 +12,7 @@ const SpaceInvaders = (() => {
1212 const ALIEN_ROWS = [ "👾" , "👽" , "🛸" , "🐙" , "👾" ] ;
1313 const GAME_ID = "space-invaders" ;
1414 const BULLET_CLEANUP_BUFFER = 40 ;
15+ let _audioContext = null ;
1516
1617 // ─── Public entry-point ──────────────────────────────────────────────────
1718
@@ -152,6 +153,7 @@ const SpaceInvaders = (() => {
152153 ( bullet , alien ) => {
153154 bullet . destroy ( ) ;
154155 alien . destroy ( ) ;
156+ _playInvaderHitSound ( ) ;
155157
156158 if ( scene . si_aliens . countActive ( ) === 0 ) {
157159 _onVictory ( scene ) ;
@@ -224,6 +226,36 @@ const SpaceInvaders = (() => {
224226 scene . si_lastFired = now ;
225227 }
226228
229+ function _playInvaderHitSound ( ) {
230+ const AudioCtx = window . AudioContext || window . webkitAudioContext ;
231+ if ( ! AudioCtx ) return ;
232+
233+ if ( ! _audioContext ) {
234+ _audioContext = new AudioCtx ( ) ;
235+ }
236+ if ( _audioContext . state === "suspended" ) {
237+ _audioContext . resume ( ) . catch ( ( ) => { } ) ;
238+ }
239+
240+ const now = _audioContext . currentTime ;
241+ const oscillator = _audioContext . createOscillator ( ) ;
242+ const gainNode = _audioContext . createGain ( ) ;
243+
244+ oscillator . type = "square" ;
245+ oscillator . frequency . setValueAtTime ( 840 , now ) ;
246+ oscillator . frequency . exponentialRampToValueAtTime ( 280 , now + 0.08 ) ;
247+
248+ gainNode . gain . setValueAtTime ( 0.0001 , now ) ;
249+ gainNode . gain . exponentialRampToValueAtTime ( 0.12 , now + 0.01 ) ;
250+ gainNode . gain . exponentialRampToValueAtTime ( 0.0001 , now + 0.09 ) ;
251+
252+ oscillator . connect ( gainNode ) ;
253+ gainNode . connect ( _audioContext . destination ) ;
254+
255+ oscillator . start ( now ) ;
256+ oscillator . stop ( now + 0.1 ) ;
257+ }
258+
227259 // ─── Victory / cleanup ────────────────────────────────────────────────────
228260
229261 function _onVictory ( scene ) {
0 commit comments