diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx
index abf8323c274fdc549393a2c6cc7f39069abe916a..69a613c1fddf4f72ea84d058eef5236c71ef2ec7 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx
@@ -5,6 +5,7 @@ import { defineMessages, intlShape, injectIntl } from 'react-intl';
 import Button from '/imports/ui/components/button/component';
 import getFromUserSettings from '/imports/ui/services/users-settings';
 import withShortcutHelper from '/imports/ui/components/shortcut-help/service';
+import MutedAlert from '/imports/ui/components/muted-alert/component';
 import { styles } from './styles';
 
 const intlMessages = defineMessages({
@@ -63,6 +64,9 @@ class AudioControls extends PureComponent {
       intl,
       shortcuts,
       isVoiceUser,
+      inputStream,
+      isViewer,
+      isPresenter,
     } = this.props;
 
     let joinIcon = 'audio_off';
@@ -74,29 +78,32 @@ class AudioControls extends PureComponent {
       }
     }
 
+    const label = muted ? intl.formatMessage(intlMessages.unmuteAudio)
+      : intl.formatMessage(intlMessages.muteAudio);
+
+    const toggleMuteBtn = (
+      <Button
+        className={cx(styles.muteToggle, !talking || styles.glow, !muted || styles.btn)}
+        onClick={handleToggleMuteMicrophone}
+        disabled={disable}
+        hideLabel
+        label={label}
+        aria-label={label}
+        color={!muted ? 'primary' : 'default'}
+        ghost={muted}
+        icon={muted ? 'mute' : 'unmute'}
+        size="lg"
+        circle
+        accessKey={shortcuts.togglemute}
+      />
+    );
+
     return (
       <span className={styles.container}>
-        {showMute && isVoiceUser
-          ? (
-            <Button
-              className={cx(styles.button, !talking || styles.glow, !muted || styles.btn)}
-              onClick={handleToggleMuteMicrophone}
-              disabled={disable}
-              hideLabel
-              label={muted ? intl.formatMessage(intlMessages.unmuteAudio)
-                : intl.formatMessage(intlMessages.muteAudio)}
-              aria-label={muted ? intl.formatMessage(intlMessages.unmuteAudio)
-                : intl.formatMessage(intlMessages.muteAudio)}
-              color={!muted ? 'primary' : 'default'}
-              ghost={muted}
-              icon={muted ? 'mute' : 'unmute'}
-              size="lg"
-              circle
-              accessKey={shortcuts.togglemute}
-            />
-          ) : null}
+        {muted ? <MutedAlert {...{ inputStream, isViewer, isPresenter }} /> : null}
+        {showMute && isVoiceUser ? toggleMuteBtn : null}
         <Button
-          className={cx(styles.button, inAudio || styles.btn)}
+          className={cx(inAudio || styles.btn)}
           onClick={inAudio ? handleLeaveAudio : handleJoinAudio}
           disabled={disable}
           hideLabel
@@ -111,7 +118,8 @@ class AudioControls extends PureComponent {
           circle
           accessKey={inAudio ? shortcuts.leaveaudio : shortcuts.joinaudio}
         />
-      </span>);
+      </span>
+    );
   }
 }
 
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
index 04161e8635cfaaa187dee9d1e6651fd7f21c69c1..d55c0d4e8e29d15319ca72fb3e7f34a1d8f34ff7 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
@@ -5,10 +5,14 @@ import AudioManager from '/imports/ui/services/audio-manager';
 import { makeCall } from '/imports/ui/services/api';
 import lockContextContainer from '/imports/ui/components/lock-viewers/context/container';
 import logger from '/imports/startup/client/logger';
+import Auth from '/imports/ui/services/auth';
+import Users from '/imports/api/users';
 import AudioControls from './component';
 import AudioModalContainer from '../audio-modal/container';
 import Service from '../service';
 
+const ROLE_VIEWER = Meteor.settings.public.user.role_viewer;
+
 const AudioControlsContainer = props => <AudioControls {...props} />;
 
 const processToggleMuteFromOutside = (e) => {
@@ -54,16 +58,30 @@ const {
   joinListenOnly,
 } = Service;
 
-export default lockContextContainer(withModalMounter(withTracker(({ mountModal, userLocks }) => ({
-  processToggleMuteFromOutside: arg => processToggleMuteFromOutside(arg),
-  showMute: isConnected() && !isListenOnly() && !isEchoTest() && !userLocks.userMic,
-  muted: isConnected() && !isListenOnly() && isMuted(),
-  inAudio: isConnected() && !isEchoTest(),
-  listenOnly: isConnected() && isListenOnly(),
-  disable: isConnecting() || isHangingUp() || !Meteor.status().connected,
-  talking: isTalking() && !isMuted(),
-  isVoiceUser: isVoiceUser(),
-  handleToggleMuteMicrophone: () => toggleMuteMicrophone(),
-  handleJoinAudio: () => (isConnected() ? joinListenOnly() : mountModal(<AudioModalContainer />)),
-  handleLeaveAudio,
-}))(AudioControlsContainer)));
+export default lockContextContainer(withModalMounter(withTracker(({ mountModal, userLocks }) => {
+  const currentUser = Users.findOne({ meetingId: Auth.meetingID, userId: Auth.userID }, {
+    fields: {
+      role: 1,
+      presenter: 1,
+    },
+  });
+  const isViewer = currentUser.role === ROLE_VIEWER;
+  const isPresenter = currentUser.presenter;
+
+  return ({
+    processToggleMuteFromOutside: arg => processToggleMuteFromOutside(arg),
+    showMute: isConnected() && !isListenOnly() && !isEchoTest() && !userLocks.userMic,
+    muted: isConnected() && !isListenOnly() && isMuted(),
+    inAudio: isConnected() && !isEchoTest(),
+    listenOnly: isConnected() && isListenOnly(),
+    disable: isConnecting() || isHangingUp() || !Meteor.status().connected,
+    talking: isTalking() && !isMuted(),
+    isVoiceUser: isVoiceUser(),
+    handleToggleMuteMicrophone: () => toggleMuteMicrophone(),
+    handleJoinAudio: () => (isConnected() ? joinListenOnly() : mountModal(<AudioModalContainer />)),
+    handleLeaveAudio,
+    inputStream: AudioManager.inputStream,
+    isViewer,
+    isPresenter,
+  });
+})(AudioControlsContainer)));
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss
index 702f765146bfd25adb75f2b88c60bb9a0c21877a..49b567556555d5461bf17d3d95c93df3ffbe82db 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss
@@ -5,31 +5,39 @@
   display: flex;
   flex-flow: row;
 
-  > * {
-    margin: 0 var(--sm-padding-x);
+  .muteToggle {
+    margin-right: var(--sm-padding-x);
+    margin-left: 0;
 
     @include mq($small-only) {
-      margin: 0 var(--sm-padding-y);
+      margin-right: var(--sm-padding-y);
     }
-  }
-
-  > :first-child {
-    margin-left: 0;
-    margin-right: inherit;
 
     [dir="rtl"] & {
-      margin-left: inherit;
+      margin-left: var(--sm-padding-x);
       margin-right: 0;
+
+      @include mq($small-only) {
+        margin-left: var(--sm-padding-y);
+      }
     }
   }
-
+  
   > :last-child {
-      margin-left: inherit;
-      margin-right: 0;
+    margin-left: var(--sm-padding-x);
+    margin-right: 0;
+
+    @include mq($small-only) {
+      margin-left: var(--sm-padding-y);
+    }
 
     [dir="rtl"] & {
       margin-left: 0;
-      margin-right: inherit;
+      margin-right: var(--sm-padding-x);
+
+      @include mq($small-only) {
+        margin-right: var(--sm-padding-y);
+      }
     }
   }
 }
diff --git a/bigbluebutton-html5/imports/ui/components/muted-alert/component.jsx b/bigbluebutton-html5/imports/ui/components/muted-alert/component.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..88d03e90e055a64df7a03c0e6ea15ee98b797aeb
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/muted-alert/component.jsx
@@ -0,0 +1,84 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+import hark from 'hark';
+import Icon from '/imports/ui/components/icon/component';
+import cx from 'classnames';
+import { styles } from './styles';
+
+const MUTE_ALERT_CONFIG = Meteor.settings.public.app.mutedAlert;
+
+const propTypes = {
+  inputStream: PropTypes.object.isRequired,
+};
+
+class MutedAlert extends Component {
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      visible: false,
+    };
+
+    this.speechEvents = null;
+    this.timer = null;
+
+    this.resetTimer = this.resetTimer.bind(this);
+  }
+
+  componentDidMount() {
+    this._isMounted = true;
+    const { inputStream } = this.props;
+    const { interval, threshold, duration } = MUTE_ALERT_CONFIG;
+    this.speechEvents = hark(inputStream, { interval, threshold });
+    this.speechEvents.on('speaking', () => {
+      this.resetTimer();
+      if (this._isMounted) this.setState({ visible: true });
+    });
+    this.speechEvents.on('stopped_speaking', () => {
+      if (this._isMounted) {
+        this.timer = setTimeout(() => this.setState(
+          { visible: false },
+        ), duration);
+      }
+    });
+  }
+
+  componentWillUnmount() {
+    this._isMounted = false;
+    if (this.speechEvents) this.speechEvents.stop();
+    this.resetTimer();
+  }
+
+  resetTimer() {
+    if (this.timer) clearTimeout(this.timer);
+    this.timer = null;
+  }
+
+  render() {
+    const { enabled } = MUTE_ALERT_CONFIG;
+    if (!enabled) return null;
+    const { isViewer, isPresenter } = this.props;
+    const { visible } = this.state;
+    const style = {};
+    style[styles.alignForMod] = !isViewer || isPresenter;
+
+    return visible ? (
+      <div className={cx(styles.muteWarning, style)}>
+        <span>
+          <FormattedMessage
+            id="app.muteWarning.label"
+            description="Warning when someone speaks while muted"
+            values={{
+              0: <Icon iconName="mute" />,
+            }}
+          />
+        </span>
+      </div>
+    ) : null;
+  }
+}
+
+MutedAlert.propTypes = propTypes;
+
+export default MutedAlert;
diff --git a/bigbluebutton-html5/imports/ui/components/muted-alert/styles.scss b/bigbluebutton-html5/imports/ui/components/muted-alert/styles.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f1076a2cb4204fea2603579e717a6f5d5a7c0f32
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/muted-alert/styles.scss
@@ -0,0 +1,28 @@
+@import "../../stylesheets/variables/_all";
+
+.muteWarning {
+  position: absolute;
+  color: var(--color-white);
+  background-color: var(--color-tip-bg);
+  text-align: center;
+  line-height: 1;
+  font-size: var(--font-size-xl);
+  padding: var(--md-padding-x);
+  border-radius: var(--border-radius);
+  top: -50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  z-index: 5;
+
+  > span {
+    white-space: nowrap;
+  }
+
+  @include mq($small-only) {
+    font-size: var(--font-size-md);;
+  }
+}
+
+.alignForMod {
+  left: 52.25%;
+}
diff --git a/bigbluebutton-html5/imports/ui/stylesheets/variables/palette.scss b/bigbluebutton-html5/imports/ui/stylesheets/variables/palette.scss
index 288d3a11fc9c312f7beeb9a597101b353af8f95b..4edfd06d5d23937fc2e0c4cf3106f6c8336891ff 100644
--- a/bigbluebutton-html5/imports/ui/stylesheets/variables/palette.scss
+++ b/bigbluebutton-html5/imports/ui/stylesheets/variables/palette.scss
@@ -30,4 +30,5 @@
   --color-gray-label: var(--color-gray);
 
   --color-transparent: #ff000000;
+  --color-tip-bg: #333333;
 }
diff --git a/bigbluebutton-html5/imports/ui/stylesheets/variables/typography.scss b/bigbluebutton-html5/imports/ui/stylesheets/variables/typography.scss
index 266cd387c3dc199427044e2aa3599f28448f7b4f..bf83f8da395fa33535242c4244189bc263016e41 100644
--- a/bigbluebutton-html5/imports/ui/stylesheets/variables/typography.scss
+++ b/bigbluebutton-html5/imports/ui/stylesheets/variables/typography.scss
@@ -6,7 +6,7 @@
   --font-family-base: var(--font-family-sans-serif);
 
   --font-size-base: 1rem;
-  --font-size-xl: 1.5rem;
+  --font-size-xl: 1.75rem;
   --font-size-large: 1.25rem;
   --font-size-md: 0.95rem; 
   --font-size-small: 0.875rem;
diff --git a/bigbluebutton-html5/package.json b/bigbluebutton-html5/package.json
index 49055c31f55e2cd834f5ac93881e9d58b7013db6..5ea8c1f511e5049ad54348de480293df55c882dc 100755
--- a/bigbluebutton-html5/package.json
+++ b/bigbluebutton-html5/package.json
@@ -44,6 +44,7 @@
     "fastdom": "^1.0.9",
     "fibers": "^4.0.2",
     "flat": "~4.1.0",
+    "hark": "^1.2.3",
     "immutability-helper": "~2.8.1",
     "langmap": "0.0.16",
     "lodash": "^4.17.19",
diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml
index cd182ac81fb0df170ed4e8074f6846ed3c267f20..839ef9abeaa926c6b7ded73fcb6743e321351fa5 100755
--- a/bigbluebutton-html5/private/config/settings.yml
+++ b/bigbluebutton-html5/private/config/settings.yml
@@ -27,6 +27,11 @@ public:
     ipv4FallbackDomain: ""
     allowLogout: true
     allowFullscreen: true
+    mutedAlert:
+      enabled: true
+      interval: 200
+      threshold: -50
+      duration: 4000
     remainingTimeThreshold: 30
     remainingTimeAlertThreshold: 1
     defaultSettings:
diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json
index 000f5750a0c003b369b8729cd4c411d9b5505ded..cc6ff15f39ec68c0c3ddd42ca91f5c63d6e96889 100755
--- a/bigbluebutton-html5/private/locales/en.json
+++ b/bigbluebutton-html5/private/locales/en.json
@@ -253,6 +253,7 @@
     "app.connectingMessage": "Connecting ...",
     "app.waitingMessage": "Disconnected. Trying to reconnect in {0} seconds ...",
     "app.retryNow": "Retry now",
+    "app.muteWarning.label": "Click {0} to unmute yourself.",
     "app.navBar.settingsDropdown.optionsLabel": "Options",
     "app.navBar.settingsDropdown.fullscreenLabel": "Make fullscreen",
     "app.navBar.settingsDropdown.settingsLabel": "Settings",