# Even when I tap on the screen in iOS, the sound still doesn't play

**URL:** <https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185>\
**Category:** Technical Support\
**Tags:** cloud-editor, general, studio\
**Created:** [August 27, 2025, 2:48pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185 "2025-08-27T14:48:29Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Aquimo\_Games](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@Aquimo\_Games](https://forum.8thwall.com/u/Aquimo_Games)\
**Post date:** [August 27, 2025, 2:48pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/1 "2025-08-27T14:48:29Z")

</div>

I’ve added sound to my project, but it doesn’t seem to play — even when I tap on it. To resume audio on iOS, I’m using a specific function. But they doesnt seem to work as well.

```c
  function unlockWorldAudio() {
      const audioSys = world.audio as any
      console.log('world audio called')

      // Some versions expose audioCtx or context
      const ctx = audioSys.ctx || audioSys.context || audioSys.audioCtx
      console.log('ctx ', ctx, ' >>>> ')
      if (ctx && ctx.state === 'suspended') {
        console.log('ctx', ctx.state)
        ctx.resume().then(() => {
          console.log('world.audio unlocked')
        })
      }
    }

    function resumeAudio(timeout = 1) {
      setTimeout(() => {
        console.log('resumeAudio')
        const context = new (window.AudioContext || (window as any).webkitAudioContext)()
        if (context && (context.state === 'suspended' || context.state === 'interrupted')) {
          context.resume().then(() => {
            console.log('resumeAudio - [audio-fix] AudioContext resumed')
          }).catch((e) => {
            console.warn('resumeAudio - [audio-fix] Resume failed:', e)
          })
        }
      }, timeout)
    }

```

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [August 27, 2025, 5:38pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/2 "2025-08-27T17:38:24Z")

</div>

Can you land your changes and [share the project](https://www.8thwall.com/docs/account/projects/project-sharing/#share-project-with-support) with the `support` workspace so I can take a look?

---

<div class="post-metadata">

**Author:** ![Aquimo\_Games](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@Aquimo\_Games](https://forum.8thwall.com/u/Aquimo_Games)\
**Post date:** [September 2, 2025, 12:22pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/3 "2025-09-02T12:22:04Z")

</div>

I’m unable to share the full project, but I’m including the relevant code snippet related to sound handling.

We’re using a PlaySound function that selects and plays audio clips based on an aid value. Each model in the experience has around 4–5 animations. Once an animation finishes, the GLTF\_ANIMATION\_FINISHED event listener triggers the next sound by calling PlaySound with the appropriate audio ID.

This setup manages sound transitions between animations. While background music plays correctly upon user interaction, some of the transition sounds fail to play consistently.

**Question:**  
Is a separate user gesture or interaction required for each sound to play, or is there a way to handle this within the existing animation flow?

Code snippet:-

```auto
function PlaySound(aid = -1, canLoop = false) {
    let soundName = '00_intro.mp3'
    aid %= 5
    switch (aid) {
        case 0:
            soundName = '01_setup.mp3'
            break

        case 1:
            soundName = '02_story.mp3'
            break

        case 2:
            soundName = '03_story.mp3'
            break

        case 3:
            soundName = '04_story.mp3'
            break

        case 4:
            soundName = '05_story.mp3'
            break

    
        default:
            soundName = '00_intro.mp3'
            break
    }

    const soundPath = `assets/sounds/${soundName}`
    ecs.Audio.set(world, soundManager, {
        url: null,
        paused: false,
        loop: canLoop,
    })

    requestAnimationFrame(() => {
        console.log('playing sound', soundName, ' >>>> ', canLoop)
        ecs.Audio.set(world, gameSoundManager, {
            url: soundPath,
            paused: false,
            loop: canLoop,
        })
    })
}
world.events.addListener(fieldModel, ecs.events.GLTF_ANIMATION_FINISHED, () => {
    console.log('event anim ended = ', animId)
    animCounter++
    animId += 1
    PlaySound(animId)
})

```

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [September 2, 2025, 4:19pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/4 "2025-09-02T16:19:29Z")

</div>

I’m not sure your requestAnimationFrame function is ever getting called. I would use a [State Machine](https://www.8thwall.com/docs/studio/essentials/state-machines/)in your Custom Component with a different state for each sound and then set the sound path onEnter for the given state. You should only need one interaction on the page for sounds to work.

---

<div class="post-metadata">

**Author:** ![Aquimo\_Games](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@Aquimo\_Games](https://forum.8thwall.com/u/Aquimo_Games)\
**Post date:** [September 2, 2025, 7:20pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/5 "2025-09-02T19:20:53Z")

</div>

Thanks for the suggestion. I actually implemented a state machine with a different state for each sound, setting the sound path in the `onEnter` of each state. However, the result is the same — some audio files play, while others still don’t.

code Snippet:-

```auto
import * as ecs from '@8thwall/ecs'
import {GlobalRef} from './GlobalReferences'

ecs.registerComponent({
  name: 'SoundManagerScript',
  schema: {
    audioCompNode: ecs.eid,
  },

  add: (world, component) => {
    GlobalRef.instance.soundManagerScript = component.eid

    // Unlock audio on first gesture (Safari/iOS requirement)
    window.addEventListener(
      'click',
      () => {
        console.log('🔓 Audio unlocked')
        world.audio.play()
      },
      {once: true}
    )
  },

  stateMachine: ({world, eid, schemaAttribute}) => {
    const {audioCompNode} = schemaAttribute.get(eid)

    // --- Utility ---
    function playSound(filename, loop = false) {

      const path = `assets/sounds/${filename}`
      console.log('🎶 Path:', path)

      // Reset audio node before playing
      ecs.Audio.set(world, audioCompNode, {
        url: path,
        paused: false,
        loop: false,
        volume: 1,
      })

    }

    // --- Define triggers ---
    const toDefault = ecs.defineTrigger()
    const toSetup = ecs.defineTrigger()
    const toStory1 = ecs.defineTrigger()
    const toStory2 = ecs.defineTrigger()
    const toStory3 = ecs.defineTrigger()
    const toStory4 = ecs.defineTrigger()
    const toHome = ecs.defineTrigger()
    const toNone = ecs.defineTrigger()

    // Put triggers in a map for wiring
    const triggerMap = {
      default: toDefault,
      setup: toSetup,
      story1: toStory1,
      story2: toStory2,
      story3: toStory3,
      story4: toStory4,
      home: toHome,
      none: toNone,
    }

    // --- Define states ---
    const defaultState = ecs.defineState('default').initial().onEnter(() => playSound('noSound.mp3'))
    const setup = ecs.defineState('setup').onEnter(() => playSound('01_setup.mp3'))
    const story1 = ecs.defineState('story1').onEnter(() => playSound('02_story.mp3'))
    const story2 = ecs.defineState('story2').onEnter(() => playSound('03_story.mp3'))
    const story3 = ecs.defineState('story3').onEnter(() => playSound('04_story.mp3'))
    const story4 = ecs.defineState('story4').onEnter(() => playSound('05_story.mp3'))
    const home = ecs.defineState('home').onEnter(() => playSound('homeMusic.mp3', true))
    const none = ecs.defineState('none').onEnter(() => playSound('noSound.mp3'))

    const allStates = [defaultState, setup, story1, story2, story3, story4, home, none]

    // --- Wire every trigger to every state (universal jumping) ---
    allStates.forEach((state) => {
      Object.entries(triggerMap).forEach(([name, trigger]) => {
        state.onTrigger(trigger, name)
      })
    })

    // --- Expose globally ---
    GlobalRef.instance.PlaySound = (stateName) => {
      const trigger = triggerMap[stateName]
      if (!trigger) {
        console.warn(`⚠️ Unknown sound state: ${stateName}`)
        return
      }

      console.log(`🎵 Switching to state: ${stateName}`)
      trigger.trigger()
    }

    return allStates
  },
})

//Function:-

  function PlaySoundBasedOnID(aid = -1, canLoop = false) {
    switch (aid) {
      case 0: // Setup
        GlobalRef.instance.PlaySound('setup')
        break
      case 1: // Story 1
        GlobalRef.instance.PlaySound('story1')
        break
      case 2: // Story 2
        GlobalRef.instance.PlaySound('story2')
        break
      case 3: // Story 3
        GlobalRef.instance.PlaySound('story3')
        break
      case 4: // Story 4
        GlobalRef.instance.PlaySound('story4')
        break
      case 100: // Home music (looping)
        GlobalRef.instance.PlaySound('home')
        break
      case 400: // No sound
        GlobalRef.instance.PlaySound('none')
        break
      default: // Default intro
        GlobalRef.instance.PlaySound('default')
        break
    }
  }

```

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [September 2, 2025, 8:22pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/6 "2025-09-02T20:22:45Z")

</div>

Is it random which file plays and which doesn’t or is it always the same file?

---

<div class="post-metadata">

**Author:** ![Aquimo\_Games](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@Aquimo\_Games](https://forum.8thwall.com/u/Aquimo_Games)\
**Post date:** [September 3, 2025, 3:53am UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/7 "2025-09-03T03:53:37Z")

</div>

80% of the time it is the same file, and the rest is random

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [September 3, 2025, 5:31pm UTC](https://forum.8thwall.com/t/even-when-i-tap-on-the-screen-in-ios-the-sound-still-doesnt-play/8185/8 "2025-09-03T17:31:20Z")

</div>

Nothing in the code stands out as a cause for this behavior. Since projects often involve many moving parts, I’m unable to fully debug the issue without access to the full project.
