Repository: streamlit/streamlit

Issue:
[Coming soon] Autoplay option for audio and video
### Request

When a user uploads an audio file via st.audio(), they then have to hit the play button to hear the audio. While this works in most cases, for interesting applications, like writing an app that takes text-to-speech, it would be reasonable to have the audio file play once automatically. 

### Solution

**MVP:** What's the smallest possible solution that would get 80% of the problem out of the way?

To have a parameter like `auto_play` that can be passed to the function such as `st.audio(file.mp4,auto_play=True)` (default is auto_play=False), that triggers the audio file to play one time after the file is finished loading.

### Additional context

The conversation that sparked this Feature Request from the [streamlit community](https://discuss.streamlit.io/t/audio-display/7806) 

---

Community voting on feature requests enables the Streamlit team to understand which features are most important to our users.

**If you'd like the Streamlit team to prioritize this feature request, please use the 👍 (thumbs up emoji) reaction in response to the initial post.**


Discussion:
> ### Request
> 
> When a user uploads an audio file via st.audio(), they then have to hit the play button to hear the audio. While this works in most cases, for interesting applications, like writing an app that takes text-to-speech, it would be reasonable to have the audio file play once automatically. 
> 
> ### Solution
> 
> **MVP:** What's the smallest possible solution that would get 80% of the problem out of the way?
> 
> To have a parameter like `auto_play` that can be passed to the function such as `st.audio(file.mp4,auto_play=True)` (default is auto_play=False), that triggers the audio file to play one time after the file is finished loading.
> 
> ### Additional context
> 
> The conversation that sparked this Feature Request from the [streamlit community](https://discuss.streamlit.io/t/audio-display/7806) 


Hi, i am trying to use autoplay but i have this problem, audio() got an unexpected keyword argument 'auto_play', I uninstalled and reinstalled streamlit in the last version (0.78) but it didnt work, appreciate all your advices. thanks.
Hi @oscar13ud, 

This is a feature request for an autoplay function, and the MVP is demoed as a simple parameter called `auto_play`.  

This is not in Streamlit's core functionality
It would be great to add `audoplay` to video file too. Could streamlit add `autoplay` atttribute when generating the html tag `video`? see also https://www.w3schools.com/tags/att_video_autoplay.asp
Hi all, it would be great to have this functionality - an auto_play parameter, default is false. There have been numerous requests for it, and it seems trivial to implement. In my application, a tts model generates audio, and it would be great to have autoplay for the generated audio. 
This would be such a useful feature!
Hi, are there any updates on the autoplay feature? I am creating a Google maps like navigation app using streamlit. I want to autoplay messages like "In 200 meters turn right". Is there a possible workaround to make the autoplay work? Thank you.
Has anyone figured out a hack around autoplay feature without showing the controls?
I tried to add just some notification sound to my app using either:
- [playsound package](https://github.com/TaylorSMarks/playsound) works locally, but on streamlit cloud the sound does not get propagated, nothing is played but no error displays neither
- `print('\007')` Which makes a sound as explained [here](https://stackoverflow.com/a/30220552/10310273)

But all methods work fine locally but not on Streamlit cloud 👎 
I found a workaround. You can use html code to do exactly this:

```py
import streamlit as st
import time

html_string = """
            <audio controls autoplay>
              <source src="https://www.orangefreesounds.com/wp-content/uploads/2022/04/Small-bell-ringing-short-sound-effect.mp3" type="audio/mp3">
            </audio>
            """

sound = st.empty()
sound.markdown(html_string, unsafe_allow_html=True)  # will display a st.audio with the sound you specified in the "src" of the html_string and autoplay it
time.sleep(2)  # wait for 2 seconds to finish the playing of the audio
sound.empty()  # optionally delete the element afterwards
```

This will display the same widget as `st.audio` but will autoplay the sound once it is created. You can also afterwards remove it again by using the `.empty()`, but you need to `time.sleep(2)` to make sure it has enough time to finish autoplaying.

This works both locally as on streamlit cloud!
Hi @tdekelver , 

Thanks for the code! That's really helpful. May I kindly ask do you know if it is possible to hide the audio button to make it a background music?

Best,
Jun
> Hi @tdekelver ,
> 
> Thanks for the code! That's really helpful. May I kindly ask do you know if it is possible to hide the audio button to make it a background music?
> 
> Best, Jun

No did not find anything... 
> I found a workaround. You can use html code to do exactly this:
> 
> ```python
> import streamlit as st
> import time
> 
> html_string = """
>             <audio controls autoplay>
>               <source src="https://www.orangefreesounds.com/wp-content/uploads/2022/04/Small-bell-ringing-short-sound-effect.mp3" type="audio/mp3">
>             </audio>
>             """
> 
> sound = st.empty()
> sound.markdown(html_string, unsafe_allow_html=True)  # will display a st.audio with the sound you specified in the "src" of the html_string and autoplay it
> time.sleep(2)  # wait for 2 seconds to finish the playing of the audio
> sound.empty()  # optionally delete the element afterwards
> ```
> 
> This will display the same widget as `st.audio` but will autoplay the sound once it is created. You can also afterwards remove it again by using the `.empty()`, but you need to `time.sleep(2)` to make sure it has enough time to finish autoplaying.
> 
> This works both locally as on streamlit cloud!

sorry, i beg your permission, what if we want to play file from local with autoplay feature?
Hi, any

> ```python
> html_string = """
>             <audio controls autoplay>
>               <source src="https://www.orangefreesounds.com/wp-content/uploads/2022/04/Small-bell-ringing-short-sound-effect.mp3" type="audio/mp3">
>             </audio>
>             """
> 
> sound = st.empty()
> sound.markdown(html_string, unsafe_allow_html=True)  # will display a st.audio with the sound you specified in the "src" of the html_string and autoplay it
> time.sleep(2)  # wait for 2 seconds to finish the playing of the audio
> sound.empty()  # op
> ```

Hey, that's a cool solution! Any idea on how to play a local audio file using the same html code?
I'm not that proficient in html so kindly pardon me.
@ayanatherate Using local audio is trickier, because normally that `src` is expecting a url. But, it also works with a base64-encoded file, like this:

```python
import base64

import streamlit as st


def autoplay_audio(file_path: str):
    with open(file_path, "rb") as f:
        data = f.read()
        b64 = base64.b64encode(data).decode()
        md = f"""
            <audio controls autoplay="true">
            <source src="data:audio/mp3;base64,{b64}" type="audio/mp3">
            </audio>
            """
        st.markdown(
            md,
            unsafe_allow_html=True,
        )


st.write("# Auto-playing Audio!")

autoplay_audio("local_audio.mp3")
```
> 

I found a solution to not show the player:
`<audio autoplay>`
instead of
`<audio controls autoplay>`
@blackary thank you for that workaround.
Is it possible to play the sound without showing the audio player widget at all?
@gustavz If you use the solution that @juanblue2022 provided (removing `controls`) it should work 
```python
import base64

import streamlit as st


def autoplay_audio(file_path: str):
    with open(file_path, "rb") as f:
        data = f.read()
        b64 = base64.b64encode(data).decode()
        md = f"""
            <audio autoplay="true">
            <source src="data:audio/mp3;base64,{b64}" type="audio/mp3">
            </audio>
            """
        st.markdown(
            md,
            unsafe_allow_html=True,
        )


st.write("# Auto-playing Audio!")

autoplay_audio("local_audio.mp3")
```
I was struggling with this as well. In my case I was producing an audio file from Python and I didn't want to save it to a file. After a lot of banging my head against the wall, this ended up working:
```python
import base64

audio_base64 = base64.b64encode(audio_bytes).decode('utf-8')
audio_tag = f'<audio autoplay="true" src="data:audio/wav;base64,{audio_base64}">'
st.markdown(audio_tag, unsafe_allow_html=True)
``` 
> @gustavz If you use the solution that @juanblue2022 provided (removing `controls`) it should work
> 
> ```python
> import base64
> 
> import streamlit as st
> 
> 
> def autoplay_audio(file_path: str):
>     with open(file_path, "rb") as f:
>         data = f.read()
>         b64 = base64.b64encode(data).decode()
>         md = f"""
>             <audio autoplay="true">
>             <source src="data:audio/mp3;base64,{b64}" type="audio/mp3">
>             </audio>
>             """
>         st.markdown(
>             md,
>             unsafe_allow_html=True,
>         )
> 
> 
> st.write("# Auto-playing Audio!")
> 
> autoplay_audio("local_audio.mp3")
> ```

There still is a problem, when I use this code for TTS audio. First time, It works. But when i type in another text ,I saw the code running, It gen the audio bytes and run this autoplay code, but there is no voice....  But refresh the page, the first time ,it works....It's weird...
I think it may have something to do with the way streamlit reruns code. I save the audio content to memory and set auto play on that. I think that because the contents are changing during the reruns it is being picked up

It doesn't work for me. I have the newest streamlit version. should I downgrade it?

Resolving patch:
diff --git a/e2e_playwright/st_audio.py b/e2e_playwright/st_audio.py
--- a/e2e_playwright/st_audio.py
+++ b/e2e_playwright/st_audio.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import time
+
 import requests
 
 import streamlit as st
@@ -24,3 +26,14 @@
 st.audio(url2, start_time=10, end_time=13)
 
 st.audio(url2, start_time=15, end_time=19, loop=True)
+
+autoplay = st.checkbox("Autoplay", value=False)
+
+if st.button("Create some elements to unmount component"):
+    for i in range(3):
+        # The sleep here is needed, because it won't unmount the
+        # component if this is too fast.
+        time.sleep(1)
+        st.write("Another element")
+
+st.audio(url2, autoplay=autoplay)
diff --git a/e2e_playwright/st_video.py b/e2e_playwright/st_video.py
--- a/e2e_playwright/st_video.py
+++ b/e2e_playwright/st_video.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import time
+
 import requests
 
 import streamlit as st
@@ -67,3 +69,25 @@
 
 # Test end time and loop mp4 video
 st.video("test_assets/sintel-short.mp4", start_time=35, end_time=39, loop=True)
+
+# Test autoplay with video
+autoplay = st.checkbox("Autoplay", value=False)
+
+if st.button("Create some elements to unmount component"):
+    for i in range(3):
+        # The sleep here is needed, because it won't unmount the
+        # component if this is too fast.
+        time.sleep(1)
+        st.write("Another element")
+
+st.video(
+    "test_assets/sintel-short.webm",
+    autoplay=autoplay,
+)
+
+# Test muted with video
+st.video(
+    "test_assets/sintel-short.webm",
+    autoplay=True,
+    muted=True,
+)
diff --git a/lib/streamlit/elements/media.py b/lib/streamlit/elements/media.py
--- a/lib/streamlit/elements/media.py
+++ b/lib/streamlit/elements/media.py
@@ -30,6 +30,8 @@
 from streamlit.proto.Video_pb2 import Video as VideoProto
 from streamlit.runtime import caching
 from streamlit.runtime.metrics_util import gather_metrics
+from streamlit.runtime.scriptrunner import get_script_run_ctx
+from streamlit.runtime.state.common import compute_widget_id
 from streamlit.time_util import time_to_seconds
 
 if TYPE_CHECKING:
@@ -69,6 +71,7 @@ def audio(
         sample_rate: int | None = None,
         end_time: MediaTime | None = None,
         loop: bool = False,
+        autoplay: bool = False,
     ) -> DeltaGenerator:
         """Display an audio player.
 
@@ -118,6 +121,11 @@ def audio(
               e.g. ``timedelta(seconds=70)``.
         loop: bool
             Whether the audio should loop playback.
+        autoplay: bool
+            Whether the audio should start playing automatically.
+            Browsers will not autoplay audio files if the user has not interacted with
+            the page yet, for example by clicking on the page while it loads.
+            Defaults to False.
 
         Examples
         --------
@@ -183,6 +191,7 @@ def audio(
             sample_rate,
             end_time,
             loop,
+            autoplay,
         )
         return self.dg._enqueue("audio", audio_proto)
 
@@ -196,6 +205,8 @@ def video(
         subtitles: SubtitleData = None,
         end_time: MediaTime | None = None,
         loop: bool = False,
+        autoplay: bool = False,
+        muted: bool = False,
     ) -> DeltaGenerator:
         """Display a video player.
 
@@ -263,6 +274,15 @@ def video(
               e.g. ``timedelta(seconds=70)``.
         loop: bool
             Whether the video should loop playback.
+        autoplay: bool
+            Whether the video should start playing automatically.
+            Browsers will not autoplay video files if the user has not interacted with
+            the page yet, for example by clicking on the page while it loads.
+            To enable autoplay without user interaction, you can set muted=True.
+            Defaults to False.
+        muted: bool
+            Whether the video should play with the audio silenced. This can be used to
+            enable autoplay without user interaction. Defaults to False.
 
         Example
         -------
@@ -314,7 +334,6 @@ def video(
            for more information.
 
         """
-
         start_time, end_time = _parse_start_time_end_time(start_time, end_time)
 
         video_proto = VideoProto()
@@ -328,6 +347,8 @@ def video(
             subtitles,
             end_time,
             loop,
+            autoplay,
+            muted,
         )
         return self.dg._enqueue("video", video_proto)
 
@@ -434,6 +455,8 @@ def marshall_video(
     subtitles: SubtitleData = None,
     end_time: int | None = None,
     loop: bool = False,
+    autoplay: bool = False,
+    muted: bool = False,
 ) -> None:
     """Marshalls a video proto, using url processors as needed.
 
@@ -467,12 +490,22 @@ def marshall_video(
             The time at which this element should stop playing
     loop: bool
         Whether the video should loop playback.
+    autoplay: bool
+        Whether the video should start playing automatically.
+        Browsers will not autoplay video files if the user has not interacted with
+        the page yet, for example by clicking on the page while it loads.
+        To enable autoplay without user interaction, you can set muted=True.
+        Defaults to False.
+    muted: bool
+        Whether the video should play with the audio silenced. This can be used to
+        enable autoplay without user interaction. Defaults to False.
     """
 
     if start_time < 0 or (end_time is not None and end_time <= start_time):
         raise StreamlitAPIException("Invalid start_time and end_time combination.")
 
     proto.start_time = start_time
+    proto.muted = muted
 
     if end_time is not None:
         proto.end_time = end_time
@@ -531,6 +564,23 @@ def marshall_video(
                     f"Failed to process the provided subtitle: {label}"
                 ) from original_err
 
+    if autoplay:
+        ctx = get_script_run_ctx()
+        proto.autoplay = autoplay
+        id = compute_widget_id(
+            "video",
+            url=proto.url,
+            mimetype=mimetype,
+            start_time=start_time,
+            end_time=end_time,
+            loop=loop,
+            autoplay=autoplay,
+            muted=muted,
+            page=ctx.page_script_hash if ctx else None,
+        )
+
+        proto.id = id
+
 
 def _parse_start_time_end_time(
     start_time: MediaTime, end_time: MediaTime | None
@@ -647,6 +697,7 @@ def marshall_audio(
     sample_rate: int | None = None,
     end_time: int | None = None,
     loop: bool = False,
+    autoplay: bool = False,
 ) -> None:
     """Marshalls an audio proto, using data and url processors as needed.
 
@@ -670,6 +721,9 @@ def marshall_audio(
         The time at which this element should stop playing
     loop: bool
         Whether the audio should loop playback.
+    autoplay : bool
+        Whether the audio should start playing automatically.
+        Browsers will not autoplay audio files if the user has not interacted with the page yet.
     """
 
     proto.start_time = start_time
@@ -685,3 +739,19 @@ def marshall_audio(
     else:
         data = _maybe_convert_to_wav_bytes(data, sample_rate)
         _marshall_av_media(coordinates, proto, data, mimetype)
+
+    if autoplay:
+        ctx = get_script_run_ctx()
+        proto.autoplay = autoplay
+        id = compute_widget_id(
+            "audio",
+            url=proto.url,
+            mimetype=mimetype,
+            start_time=start_time,
+            sample_rate=sample_rate,
+            end_time=end_time,
+            loop=loop,
+            autoplay=autoplay,
+            page=ctx.page_script_hash if ctx else None,
+        )
+        proto.id = id


Test patch:
diff --git a/e2e_playwright/st_audio_test.py b/e2e_playwright/st_audio_test.py
--- a/e2e_playwright/st_audio_test.py
+++ b/e2e_playwright/st_audio_test.py
@@ -16,13 +16,13 @@
 import pytest
 from playwright.sync_api import Page, expect
 
-from e2e_playwright.conftest import wait_until
+from e2e_playwright.conftest import ImageCompareFunction, wait_for_app_run, wait_until
 
 
 def test_audio_has_correct_properties(app: Page):
     """Test that `st.audio` renders correct properties."""
     audio_elements = app.get_by_test_id("stAudio")
-    expect(audio_elements).to_have_count(3)
+    expect(audio_elements).to_have_count(4)
 
     expect(audio_elements.nth(0)).to_be_visible()
     expect(audio_elements.nth(0)).to_have_attribute("controls", "")
@@ -33,7 +33,7 @@ def test_audio_has_correct_properties(app: Page):
 def test_audio_end_time(app: Page):
     """Test that `st.audio` end_time property works correctly."""
     audio_elements = app.get_by_test_id("stAudio")
-    expect(audio_elements).to_have_count(3)
+    expect(audio_elements).to_have_count(4)
 
     expect(audio_elements.nth(1)).to_be_visible()
 
@@ -48,7 +48,7 @@ def test_audio_end_time(app: Page):
 def test_audio_end_time_loop(app: Page):
     """Test that `st.audio` end_time and loop properties work correctly."""
     audio_elements = app.get_by_test_id("stAudio")
-    expect(audio_elements).to_have_count(3)
+    expect(audio_elements).to_have_count(4)
 
     expect(audio_elements.nth(2)).to_be_visible()
 
@@ -61,3 +61,55 @@ def test_audio_end_time_loop(app: Page):
     app.wait_for_timeout(6000)
     expect(audio_element).to_have_js_property("paused", False)
     wait_until(app, lambda: 16 < audio_element.evaluate("el => el.currentTime") < 18)
+
+
+def test_audio_autoplay(app: Page):
+    """Test that `st.audio` autoplay property works correctly."""
+    audio_elements = app.get_by_test_id("stAudio")
+    expect(audio_elements).to_have_count(4)
+
+    expect(audio_elements.nth(3)).to_be_visible()
+
+    audio_element = audio_elements.nth(3)
+    expect(audio_element).to_have_js_property("paused", True)
+    expect(audio_element).to_have_js_property("autoplay", False)
+
+    checkbox_element = app.get_by_test_id("stCheckbox")
+    autoplay_checkbox = checkbox_element.nth(0)
+    autoplay_checkbox.click()
+    # To prevent flakiness, we wait for the audio to load and start playing
+    wait_until(app, lambda: audio_element.evaluate("el => el.readyState") == 4)
+    expect(audio_element).to_have_js_property("autoplay", True)
+    expect(audio_element).to_have_js_property("paused", False)
+
+
+def test_audio_remount_no_autoplay(app: Page):
+    """Test that `st.audio` remounts correctly without autoplay."""
+    audio_elements = app.get_by_test_id("stAudio")
+    expect(audio_elements).to_have_count(4)
+
+    expect(audio_elements.nth(3)).to_be_visible()
+
+    audio_element = audio_elements.nth(3)
+    expect(audio_element).to_have_js_property("paused", True)
+    expect(audio_element).to_have_js_property("autoplay", False)
+
+    checkbox_element = app.get_by_test_id("stCheckbox")
+    autoplay_checkbox = checkbox_element.nth(0)
+    autoplay_checkbox.click()
+    # To prevent flakiness, we wait for the audio to load and start playing
+    wait_until(app, lambda: audio_element.evaluate("el => el.readyState") == 4)
+    expect(audio_element).to_have_js_property("autoplay", True)
+    expect(audio_element).to_have_js_property("paused", False)
+
+    autoplay_checkbox.click()
+
+    button_element = app.get_by_test_id("stButton").locator("button").first
+    expect(app.get_by_test_id("stMarkdownContainer").nth(1)).to_have_text(
+        "Create some elements to unmount component"
+    )
+    button_element.click()
+    wait_for_app_run(app)
+
+    expect(audio_element).to_have_js_property("autoplay", False)
+    expect(audio_element).to_have_js_property("paused", True)
diff --git a/e2e_playwright/st_video_test.py b/e2e_playwright/st_video_test.py
--- a/e2e_playwright/st_video_test.py
+++ b/e2e_playwright/st_video_test.py
@@ -16,9 +16,9 @@
 import pytest
 from playwright.sync_api import Page, expect
 
-from e2e_playwright.conftest import ImageCompareFunction, wait_until
+from e2e_playwright.conftest import ImageCompareFunction, wait_for_app_run, wait_until
 
-VIDEO_ELEMENTS_COUNT = 9
+VIDEO_ELEMENTS_COUNT = 11
 
 # Chromium miss codecs required to play that mp3 videos
 # https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/
@@ -145,3 +145,73 @@ def test_video_end_time_loop(app: Page, nth_element: int):
     app.wait_for_timeout(6000)
     expect(video_element).to_have_js_property("paused", False)
     wait_until(app, lambda: 36 < video_element.evaluate("el => el.currentTime") < 38)
+
+
+def test_video_autoplay(app: Page):
+    """Test that `st.video` autoplay property works correctly."""
+    video_elements = app.get_by_test_id("stVideo")
+    expect(video_elements).to_have_count(VIDEO_ELEMENTS_COUNT)
+
+    expect(video_elements.nth(9)).to_be_visible()
+
+    video_element = video_elements.nth(9)
+    video_element.scroll_into_view_if_needed()
+    expect(video_element).to_have_js_property("paused", True)
+    expect(video_element).to_have_js_property("autoplay", False)
+
+    checkbox_elements = app.get_by_test_id("stCheckbox")
+    autoplay_checkbox = checkbox_elements.nth(0)
+    autoplay_checkbox.click()
+    # To prevent flakiness, we wait for the video to load and start playing
+    wait_until(app, lambda: video_element.evaluate("el => el.readyState") == 4)
+    expect(video_element).to_have_js_property("autoplay", True)
+    expect(video_element).to_have_js_property("paused", False)
+
+
+def test_video_muted_autoplay(app: Page):
+    """Test that `st.video` muted and autoplay properties work correctly."""
+    video_elements = app.get_by_test_id("stVideo")
+    expect(video_elements).to_have_count(VIDEO_ELEMENTS_COUNT)
+
+    expect(video_elements.nth(10)).to_be_visible()
+
+    video_element = video_elements.nth(10)
+    video_element.scroll_into_view_if_needed()
+
+    # To prevent flakiness, we wait for the video to load and start playing
+    wait_until(app, lambda: video_element.evaluate("el => el.readyState") == 4)
+    expect(video_element).to_have_js_property("muted", True)
+    expect(video_element).to_have_js_property("autoplay", True)
+    expect(video_element).to_have_js_property("paused", False)
+
+
+def test_video_remount_no_autoplay(app: Page):
+    """Test that `st.video` remounts correctly without autoplay."""
+    video_elements = app.get_by_test_id("stVideo")
+    expect(video_elements).to_have_count(VIDEO_ELEMENTS_COUNT)
+
+    expect(video_elements.nth(9)).to_be_visible()
+
+    video_element = video_elements.nth(9)
+    expect(video_element).to_have_js_property("paused", True)
+    expect(video_element).to_have_js_property("autoplay", False)
+
+    checkbox_elements = app.get_by_test_id("stCheckbox")
+    autoplay_checkbox = checkbox_elements.nth(0)
+    autoplay_checkbox.click()
+    # To prevent flakiness, we wait for the video to load and start playing
+    wait_until(app, lambda: video_element.evaluate("el => el.readyState") == 4)
+    expect(video_element).to_have_js_property("autoplay", True)
+    expect(video_element).to_have_js_property("paused", False)
+
+    autoplay_checkbox.click()
+
+    button_element = app.get_by_test_id("stButton").locator("button").first
+    expect(app.get_by_test_id("stMarkdownContainer").nth(2)).to_have_text(
+        "Create some elements to unmount component"
+    )
+    button_element.click()
+    wait_for_app_run(app)
+
+    expect(video_element).to_have_js_property("autoplay", False)
+    expect(video_element).to_have_js_property("paused", True)
diff --git a/frontend/lib/src/components/elements/Audio/Audio.test.tsx b/frontend/lib/src/components/elements/Audio/Audio.test.tsx
--- a/frontend/lib/src/components/elements/Audio/Audio.test.tsx
+++ b/frontend/lib/src/components/elements/Audio/Audio.test.tsx
@@ -22,10 +22,20 @@ import { render } from "@streamlit/lib/src/test_util"
 import { Audio as AudioProto } from "@streamlit/lib/src/proto"
 import { mockEndpoints } from "@streamlit/lib/src/mocks/mocks"
 import Audio, { AudioProps } from "./Audio"
+import { WidgetStateManager as ElementStateManager } from "@streamlit/lib/src/WidgetStateManager"
 
 describe("Audio Element", () => {
   const buildMediaURL = jest.fn().mockReturnValue("https://mock.media.url")
 
+  const mockSetElementState = jest.fn()
+  const mockGetElementState = jest.fn()
+  const elementMgrMock = {
+    setElementState: mockSetElementState,
+    getElementState: mockGetElementState,
+    sendRerunBackMsg: jest.fn(),
+    formsDataChanged: jest.fn(),
+  }
+
   const getProps = (elementProps: Partial<AudioProto> = {}): AudioProps => ({
     element: AudioProto.create({
       startTime: 0,
@@ -34,6 +44,7 @@ describe("Audio Element", () => {
     }),
     endpoints: mockEndpoints({ buildMediaURL: buildMediaURL }),
     width: 0,
+    elementMgr: elementMgrMock as unknown as ElementStateManager,
   })
 
   it("renders without crashing", () => {
@@ -53,6 +64,47 @@ describe("Audio Element", () => {
     expect(audioElement).toHaveAttribute("src", "https://mock.media.url")
   })
 
+  beforeEach(() => {
+    jest.clearAllMocks()
+    mockGetElementState.mockReturnValue(false) // By default, assume autoplay is not prevented
+  })
+
+  it("does not autoplay if preventAutoplay is set", () => {
+    mockGetElementState.mockReturnValueOnce(true) // Autoplay should be prevented
+    const props = getProps({ autoplay: true, id: "uniqueAudioId" })
+    render(<Audio {...props} />)
+    const audioElement = screen.getByTestId("stAudio")
+    expect(audioElement).not.toHaveAttribute("autoPlay")
+  })
+
+  it("autoplays if preventAutoplay is not set and autoplay is true", () => {
+    mockGetElementState.mockReturnValueOnce(false) // Autoplay is not prevented
+    const props = getProps({ autoplay: true, id: "uniqueAudioId" })
+    render(<Audio {...props} />)
+    const audioElement = screen.getByTestId("stAudio")
+    expect(audioElement).toHaveAttribute("autoPlay")
+  })
+
+  it("calls setElementState to prevent future autoplay on first autoplay", () => {
+    mockGetElementState.mockReturnValueOnce(false) // Autoplay is not prevented initially
+    const props = getProps({ autoplay: true, id: "uniqueAudioId" })
+    render(<Audio {...props} />)
+    expect(mockSetElementState).toHaveBeenCalledTimes(1)
+    expect(mockSetElementState).toHaveBeenCalledWith(
+      props.element.id,
+      "preventAutoplay",
+      true
+    )
+  })
+
+  // Test to ensure that setElementState is not called again if autoplay is already prevented
+  it("does not call setElementState again if autoplay is already prevented", () => {
+    mockGetElementState.mockReturnValueOnce(true) // Autoplay is already prevented
+    const props = getProps({ autoplay: true, id: "uniqueAudioId" })
+    render(<Audio {...props} />)
+    expect(mockSetElementState).not.toHaveBeenCalled()
+  })
+
   it("updates time when the prop is changed", () => {
     const props = getProps({
       url: "http://localhost:80/media/sound.wav",
diff --git a/frontend/lib/src/components/elements/Video/Video.test.tsx b/frontend/lib/src/components/elements/Video/Video.test.tsx
--- a/frontend/lib/src/components/elements/Video/Video.test.tsx
+++ b/frontend/lib/src/components/elements/Video/Video.test.tsx
@@ -20,12 +20,22 @@ import { screen } from "@testing-library/react"
 import { render } from "@streamlit/lib/src/test_util"
 import { Video as VideoProto } from "@streamlit/lib/src/proto"
 import { mockEndpoints } from "@streamlit/lib/src/mocks/mocks"
+import { WidgetStateManager as ElementStateManager } from "@streamlit/lib/src/WidgetStateManager"
 
 import Video, { VideoProps } from "./Video"
 
 describe("Video Element", () => {
   const buildMediaURL = jest.fn().mockReturnValue("https://mock.media.url")
 
+  const mockSetElementState = jest.fn()
+  const mockGetElementState = jest.fn()
+  const elementMgrMock = {
+    setElementState: mockSetElementState,
+    getElementState: mockGetElementState,
+    sendRerunBackMsg: jest.fn(),
+    formsDataChanged: jest.fn(),
+  }
+
   const getProps = (elementProps: Partial<VideoProto> = {}): VideoProps => ({
     element: VideoProto.create({
       url: "https://www.w3schools.com/html/mov_bbb.mp4",
@@ -35,6 +45,7 @@ describe("Video Element", () => {
     }),
     endpoints: mockEndpoints({ buildMediaURL: buildMediaURL }),
     width: 0,
+    elementMgr: elementMgrMock as unknown as ElementStateManager,
   })
 
   beforeEach(() => {
@@ -73,6 +84,47 @@ describe("Video Element", () => {
     )
   })
 
+  beforeEach(() => {
+    jest.clearAllMocks()
+    mockGetElementState.mockReturnValue(false) // By default, assume autoplay is not prevented
+  })
+
+  it("does not autoplay if preventAutoplay is set", () => {
+    mockGetElementState.mockReturnValueOnce(true) // Autoplay should be prevented
+    const props = getProps({ autoplay: true, id: "uniqueVideoId" })
+    render(<Video {...props} />)
+    const audioElement = screen.getByTestId("stVideo")
+    expect(audioElement).not.toHaveAttribute("autoPlay")
+  })
+
+  it("autoplays if preventAutoplay is not set and autoplay is true", () => {
+    mockGetElementState.mockReturnValueOnce(false) // Autoplay is not prevented
+    const props = getProps({ autoplay: true, id: "uniqueVideoId" })
+    render(<Video {...props} />)
+    const audioElement = screen.getByTestId("stVideo")
+    expect(audioElement).toHaveAttribute("autoPlay")
+  })
+
+  it("calls setElementState to prevent future autoplay on first autoplay", () => {
+    mockGetElementState.mockReturnValueOnce(false) // Autoplay is not prevented initially
+    const props = getProps({ autoplay: true, id: "uniqueVideoId" })
+    render(<Video {...props} />)
+    expect(mockSetElementState).toHaveBeenCalledTimes(1)
+    expect(mockSetElementState).toHaveBeenCalledWith(
+      props.element.id,
+      "preventAutoplay",
+      true
+    )
+  })
+
+  // Test to ensure that setElementState is not called again if autoplay is already prevented
+  it("does not call setElementState again if autoplay is already prevented", () => {
+    mockGetElementState.mockReturnValueOnce(true) // Autoplay is already prevented
+    const props = getProps({ autoplay: true, id: "uniqueVideoId" })
+    render(<Video {...props} />)
+    expect(mockSetElementState).not.toHaveBeenCalled()
+  })
+
   describe("YouTube", () => {
     it("renders a youtube iframe", () => {
       const props = getProps({
diff --git a/lib/tests/streamlit/elements/audio_test.py b/lib/tests/streamlit/elements/audio_test.py
--- a/lib/tests/streamlit/elements/audio_test.py
+++ b/lib/tests/streamlit/elements/audio_test.py
@@ -270,15 +270,30 @@ def test_st_audio_options(self):
             start_time=10,
             end_time=21,
             loop=True,
+            autoplay=True,
         )
 
         el = self.get_delta_from_queue().new_element
         self.assertEqual(el.audio.start_time, 10)
         self.assertEqual(el.audio.end_time, 21)
-        self.assertEqual(el.audio.loop, True)
+        self.assertTrue(el.audio.loop)
+        self.assertTrue(el.audio.autoplay)
         self.assertTrue(el.audio.url.startswith(MEDIA_ENDPOINT))
         self.assertTrue(_calculate_file_id(fake_audio_data, "audio/mp3"), el.audio.url)
 
+    def test_st_audio_just_data(self):
+        """Test st.audio with just data specified."""
+        fake_audio_data = "\x11\x22\x33\x44\x55\x66".encode("utf-8")
+        st.audio(fake_audio_data)
+
+        el = self.get_delta_from_queue().new_element
+        self.assertEqual(el.audio.start_time, 0)
+        self.assertEqual(el.audio.end_time, 0)
+        self.assertFalse(el.audio.loop)
+        self.assertFalse(el.audio.autoplay)
+        self.assertTrue(el.audio.url.startswith(MEDIA_ENDPOINT))
+        self.assertTrue(_calculate_file_id(fake_audio_data, "audio/wav"), el.audio.url)
+
     @parameterized.expand(
         [
             ("1s", None, (1, None)),
diff --git a/lib/tests/streamlit/elements/help_test.py b/lib/tests/streamlit/elements/help_test.py
--- a/lib/tests/streamlit/elements/help_test.py
+++ b/lib/tests/streamlit/elements/help_test.py
@@ -109,7 +109,7 @@ def test_deltagenerator_func(self):
         self.assertEqual("st.audio", ds.name)
         self.assertEqual("method", ds.type)
 
-        signature = "(data: 'MediaData', format: 'str' = 'audio/wav', start_time: 'MediaTime' = 0, *, sample_rate: 'int | None' = None, end_time: 'MediaTime | None' = None, loop: 'bool' = False) -> 'DeltaGenerator'"
+        signature = "(data: 'MediaData', format: 'str' = 'audio/wav', start_time: 'MediaTime' = 0, *, sample_rate: 'int | None' = None, end_time: 'MediaTime | None' = None, loop: 'bool' = False, autoplay: 'bool' = False) -> 'DeltaGenerator'"
 
         self.assertEqual(
             f"streamlit.delta_generator.MediaMixin.audio{signature}", ds.value
diff --git a/lib/tests/streamlit/elements/video_test.py b/lib/tests/streamlit/elements/video_test.py
--- a/lib/tests/streamlit/elements/video_test.py
+++ b/lib/tests/streamlit/elements/video_test.py
@@ -104,13 +104,35 @@ def test_st_video_options(self):
         """Test st.video with options."""
         fake_video_data = "\x11\x22\x33\x44\x55\x66".encode("utf-8")
         st.video(
-            fake_video_data, format="video/mp4", start_time=10, end_time=18, loop=True
+            fake_video_data,
+            format="video/mp4",
+            start_time=10,
+            end_time=18,
+            loop=True,
+            autoplay=True,
+            muted=True,
         )
 
         el = self.get_delta_from_queue().new_element
         self.assertEqual(el.video.start_time, 10)
         self.assertEqual(el.video.end_time, 18)
         self.assertTrue(el.video.loop)
+        self.assertTrue(el.video.autoplay)
+        self.assertTrue(el.video.muted)
+        self.assertTrue(el.video.url.startswith(MEDIA_ENDPOINT))
+        self.assertIn(_calculate_file_id(fake_video_data, "video/mp4"), el.video.url)
+
+    def test_st_video_just_data(self):
+        """Test st.video with just data specified."""
+        fake_video_data = "\x11\x22\x33\x44\x55\x66".encode("utf-8")
+        st.video(fake_video_data)
+
+        el = self.get_delta_from_queue().new_element
+        self.assertEqual(el.video.start_time, 0)
+        self.assertEqual(el.video.end_time, 0)
+        self.assertFalse(el.video.loop)
+        self.assertFalse(el.video.autoplay)
+        self.assertFalse(el.video.muted)
         self.assertTrue(el.video.url.startswith(MEDIA_ENDPOINT))
         self.assertIn(_calculate_file_id(fake_video_data, "video/mp4"), el.video.url)