How to Play Sound in Frappe Using JavaScript: 3 Simple Methods

Adding sound effects to your ERPNext/Frappe interface can greatly improve user interaction, feedback, and even bring a little fun into routine workflows. In this article, we’ll walk through three professional ways to play audio on button click within a Frappe form.

We’ll cover:

  • Method 1: Using frappe.utils.play_sound() with hooks.py
  • Method 2: Manually embedding and playing audio
  • Method 3: Using the browser’s Text-to-Speech API to speak messages

:white_check_mark: Method 1: Using frappe.utils.play_sound() with hooks.py


This is the cleanest and Frappe-native approach. It allows you to register sound assets in your hooks.py file and play them with just a single line of JavaScript.
:file_folder: Folder Structure

custom_module/
└── public/
    └── sounds/
        └── sound.mp3

:hammer_and_wrench: hooks.py

sounds = [
    {
        "name": "test",
        "src": "/assets/custom_module/sounds/sound.mp3",
        "volume": 0.1
    }
]

:brain:JavaScript

frappe.ui.form.on('Doctype', {
	refresh(frm) {
		frm.add_custom_button(__('Play Sound'), function() {
			frappe.utils.play_sound('test');
		});
	}
});

It’s efficient, integrates into Frappe’s asset system, and is easy to reuse across your app.

:arrows_counterclockwise: Method 2: Dynamic Audio Element with Cache Busting


Want more control? This method creates an <audio> tag on the fly and plays it. We also add a unique timestamp to prevent caching issues.
:file_folder: Folder Structure

custom_module/
└── public/
    └── sounds/
        └── sound.mp3

:brain:JavaScript

frappe.ui.form.on('Doctype', {
	refresh(frm) {
		frm.add_custom_button(__('Play Sound'), function() {
			var audio = document.createElement('audio');
			var uniqueTimestamp = new Date().getTime();

			audio.src = '/assets/custom_module/sounds/sound.mp3?timestamp=' + uniqueTimestamp;

			audio.play().catch(function(error) {
				console.error("Playback failed:", error);
			});
		});
	}
});

Ideal when you’re not registering the sound in hooks.py, or when you want to dynamically control the file source.

:speaking_head: Method 3: Speak with a Siri-like Voice


This method uses the browser’s SpeechSynthesis API to read out a response like “I am fine” when a button is clicked — great for making your UI more interactive or accessible.
:brain:JavaScript

frappe.ui.form.on('Doctype', {
    refresh: function(frm) {
        frm.add_custom_button(__('How are you?'), function() {
            function speakWithSiriVoice() {
                const msg = new SpeechSynthesisUtterance("I am fine");

                const voices = speechSynthesis.getVoices();
                const siriLikeVoice = voices.find(voice =>
                    voice.name.toLowerCase().includes("samantha") ||
                    voice.name.toLowerCase().includes("karen") ||
                    voice.name.toLowerCase().includes("moira")
                );

                msg.voice = siriLikeVoice || voices[0];
                msg.pitch = 1;
                msg.rate = 1;

                window.speechSynthesis.speak(msg);
            }

            if (speechSynthesis.getVoices().length === 0) {
                speechSynthesis.onvoiceschanged = () => {
                    speakWithSiriVoice();
                };
            } else {
                speakWithSiriVoice();
            }
        });
    }
});

It doesn’t require any audio files — pure voice generation from text. Great for greetings, confirmations, or fun user feedback.

Thank You!

2 Likes