diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx
index 2238d77940e7e8bca3bc211f29c6580c6ec237c2..2d52b94a0b8cefd37156434909d66f86d93ef5ce 100755
--- a/bigbluebutton-html5/imports/startup/client/base.jsx
+++ b/bigbluebutton-html5/imports/startup/client/base.jsx
@@ -11,12 +11,12 @@ import AudioManager from '/imports/ui/services/audio-manager';
 import logger from '/imports/startup/client/logger';
 import Users from '/imports/api/users';
 import { Session } from 'meteor/session';
+import { FormattedMessage } from 'react-intl';
 import IntlStartup from './intl';
 import Meetings, { RecordMeetings } from '../../api/meetings';
 import AppService from '/imports/ui/components/app/service';
 import Breakouts from '/imports/api/breakouts';
 import AudioService from '/imports/ui/components/audio/service';
-import { FormattedMessage } from 'react-intl';
 import { notify } from '/imports/ui/services/notification';
 
 const BREAKOUT_END_NOTIFY_DELAY = 50;
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index 0e801cf28c9b2a9c1d9b17e40e1640850a3de84c..b6775edcc5a13ef3347eecd42cb263ede6951d55 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -21,6 +21,7 @@ import LockNotifier from '/imports/ui/components/lock-viewers/notify/container';
 import PingPongContainer from '/imports/ui/components/ping-pong/container';
 import MediaService from '/imports/ui/components/media/service';
 import ManyWebcamsNotifier from '/imports/ui/components/video-provider/many-users-notify/container';
+import { withDraggableContext } from '../media/webcam-draggable-overlay/context';
 import { styles } from './styles';
 
 const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
@@ -103,6 +104,7 @@ class App extends Component {
 
     this.handleWindowResize = throttle(this.handleWindowResize).bind(this);
     this.shouldAriaHide = this.shouldAriaHide.bind(this);
+    this.renderMedia = withDraggableContext(this.renderMedia.bind(this));
   }
 
   componentDidMount() {
diff --git a/bigbluebutton-html5/imports/ui/components/media/component.jsx b/bigbluebutton-html5/imports/ui/components/media/component.jsx
index 4fb9cd56e30e29ad2fefcf69382ad9eaa47c641a..12e22229492fb71400fc8664893091ecb0f3a98f 100644
--- a/bigbluebutton-html5/imports/ui/components/media/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/media/component.jsx
@@ -1,10 +1,12 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import cx from 'classnames';
+import { isMobile, isIPad13 } from 'react-device-detect';
 import WebcamDraggable from './webcam-draggable-overlay/component';
-
 import { styles } from './styles';
 
+const BROWSER_ISMOBILE = isMobile || isIPad13;
+
 const propTypes = {
   children: PropTypes.element.isRequired,
   usersVideo: PropTypes.arrayOf(Array),
@@ -58,17 +60,36 @@ export default class Media extends Component {
       [styles.floatingOverlay]: (webcamPlacement === 'floating'),
     });
 
+    const containerClassName = cx({
+      [styles.containerV]: webcamPlacement === 'top' || webcamPlacement === 'bottom' || webcamPlacement === 'floating',
+      [styles.containerH]: webcamPlacement === 'left' || webcamPlacement === 'right',
+    });
+
     return (
       <div
         id="container"
-        className={cx(styles.container)}
+        className={containerClassName}
         ref={this.refContainer}
       >
         <div
           className={!swapLayout ? contentClassName : overlayClassName}
           style={{
-            maxHeight: usersVideo.length < 1 || (webcamPlacement === 'floating') ? '100%' : '80%',
-            minHeight: '20%',
+            maxHeight: (
+              webcamPlacement === 'left'
+              || webcamPlacement === 'right'
+              || webcamPlacement === 'floating'
+            )
+              ? '100%'
+              : '80%',
+            minHeight: BROWSER_ISMOBILE && window.innerWidth > window.innerHeight ? '50%' : '20%',
+            maxWidth: (
+              webcamPlacement === 'top'
+              || webcamPlacement === 'bottom'
+              || webcamPlacement === 'floating'
+            )
+              ? '100%'
+              : '80%',
+            minWidth: '20%',
           }}
         >
           {children}
diff --git a/bigbluebutton-html5/imports/ui/components/media/styles.scss b/bigbluebutton-html5/imports/ui/components/media/styles.scss
index 7b9597f90d19e0e2a81e4e2a754e8cc71e5db659..4e838b014f0f05732b0ff45e3f892189e9abe8a6 100644
--- a/bigbluebutton-html5/imports/ui/components/media/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/media/styles.scss
@@ -1,5 +1,6 @@
 @import "../../stylesheets/variables/_all";
 @import "../../stylesheets/variables/video";
+@import "../video-provider/video-list/styles";
 
 $content-order: 2;
 $before-content-order: 1;
@@ -24,10 +25,19 @@ $after-content-order: 3;
 }
 
 .container {
+  overflow: hidden;
+}
+
+.containerV {
   @extend %container;
   flex-direction: column;
 }
 
+.containerH {
+  @extend %container;
+  flex-direction: row;
+}
+
 .content {
   display: flex;
   align-self: stretch;
@@ -65,6 +75,26 @@ $after-content-order: 3;
   order: $after-content-order !important;
 }
 
+%overlayToRL {
+  min-width: 20%;
+  margin-left: 10px !important;
+  margin-right: 10px !important;
+}
+
+.overlayToRight {
+  @extend %overlayToRL;
+  order: 2 !important;
+}
+
+.overlayToLeft {
+  @extend %overlayToRL;
+  order: 0 !important;
+}
+
+.overlayToBottom {
+  order: 2 !important;
+}
+
 .hideOverlay {
   visibility: hidden !important;
   position: absolute !important;
@@ -91,6 +121,10 @@ $after-content-order: 3;
 .autoWidth {
   min-width: calc(var(--video-height) * var(--video-ratio)) !important;
   max-width: 100%;
+
+  .videoCanvas{
+    position: relative;
+  }
 }
 
 .fullWidth {
@@ -99,6 +133,12 @@ $after-content-order: 3;
   max-width: 100%;
 }
 
+.fullHeight {
+  height: 100% !important;
+  min-height: 100% !important;
+  max-height: 100%;
+}
+
 .hide {
   display: none;
 }
@@ -120,27 +160,21 @@ $after-content-order: 3;
 %dropZoneTopBottom {
   @extend %dropZone;
   width: 100%;
-
-  &:hover {
-    background-color: rgba(255, 255, 255, .3);
-  }
 }
 
 %dropZoneBg {
   z-index: 99;
   width: 100%;
   height: 100%;
+  &:hover {
+    background-color: rgba(255, 255, 255, .3);
+  }
 }
 
 %dropZoneBgTopBottom {
   z-index: 99;
   width: 100%;
   height: 100%;
-  background-color: rgba(255, 255, 255, .3);
-
-  &:hover {
-    background-color: rgba(255, 255, 255, .3);
-  }
 }
 
 .dropZoneTop {
@@ -148,30 +182,71 @@ $after-content-order: 3;
   top: 0;
 }
 
+.dropZoneLeft {
+  @extend %dropZone;
+  left: 0;
+}
+
 .dropZoneBottom {
   @extend %dropZoneTopBottom;
   bottom: 0;
 }
 
+.dropZoneRight {
+  @extend %dropZone;
+  right: 0;
+}
+
 .dropZoneBgTop {
   @extend %dropZoneBg;
   top: 0;
 }
 
+.dropZoneBgLeft {
+  @extend %dropZoneBg;
+  left: 0;
+}
+
 .dropZoneBgBottom {
   @extend %dropZoneBg;
   bottom: 0;
 }
 
-.resizable {
+.dropZoneBgRight {
+  @extend %dropZoneBg;
+  right: 0;
+}
+
+%resizableTopBottom {
   width: 100% !important;
+  &:hover{
+    background-color: rgba(255, 255, 255, .3);
+  }
 }
 
+%resizableLeftRight {
+  height: 100% !important;
+  &:hover{
+    background-color: rgba(255, 255, 255, .3);
+  }
+}
 
-span[class^=resizeWrapper] {
+.overlayToTop span[class^=resizeWrapper],
+.overlayToBottom span[class^=resizeWrapper] {
   div {
+    @extend %resizableTopBottom;
     height: 15px !important;
     z-index: 1;
     bottom: -10px !important;
   }
+}
+
+.overlayToLeft span[class^=resizeWrapper],
+.overlayToRight span[class^=resizeWrapper] {
+  div {
+    @extend %resizableLeftRight;
+    width: 15px !important;
+    z-index: 1;
+    bottom: -10px !important;
+  }
 }
\ No newline at end of file
diff --git a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx
index ed522e419d4d9561cef3e433dffbfa00704e5834..edb1b405e9d70173a96ea6ed80758eef381c6aa8 100644
--- a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx
@@ -1,21 +1,19 @@
-import React, { Component, Fragment } from 'react';
+import React, { PureComponent, Fragment } from 'react';
 import Draggable from 'react-draggable';
 import cx from 'classnames';
 import _ from 'lodash';
-import browser from 'browser-detect';
 import PropTypes from 'prop-types';
 import Resizable from 're-resizable';
-import { withDraggableContext } from './context';
+import { isMobile, isIPad13 } from 'react-device-detect';
+import { withDraggableConsumer } from './context';
 import VideoProviderContainer from '/imports/ui/components/video-provider/container';
 import { styles } from '../styles.scss';
 import Storage from '../../../services/storage/session';
 
-const { webcamsDefaultPlacement } = Meteor.settings.public.layout;
-const BROWSER_ISMOBILE = browser().mobile;
+const BROWSER_ISMOBILE = isMobile || isIPad13;
 
 const propTypes = {
   swapLayout: PropTypes.bool,
-  singleWebcam: PropTypes.bool,
   hideOverlay: PropTypes.bool,
   disableVideo: PropTypes.bool,
   audioModalIsOpen: PropTypes.bool,
@@ -26,7 +24,6 @@ const propTypes = {
 
 const defaultProps = {
   swapLayout: false,
-  singleWebcam: true,
   hideOverlay: false,
   disableVideo: false,
   audioModalIsOpen: false,
@@ -34,34 +31,59 @@ const defaultProps = {
 };
 const dispatchResizeEvent = () => window.dispatchEvent(new Event('resize'));
 
-class WebcamDraggable extends Component {
+class WebcamDraggable extends PureComponent {
   constructor(props) {
     super(props);
 
     this.handleWebcamDragStart = this.handleWebcamDragStart.bind(this);
     this.handleWebcamDragStop = this.handleWebcamDragStop.bind(this);
     this.onFullscreenChange = this.onFullscreenChange.bind(this);
-    this.debouncedOnResize = _.debounce(this.onResize.bind(this), 500);
+    this.debouncedOnResize = _.debounce(this.onWindowResize.bind(this), 500);
     this.onResizeStop = this.onResizeStop.bind(this);
+    this.onResizeStart = this.onResizeStart.bind(this);
+    this.setPlacementPercent = this.setPlacementPercent.bind(this);
+    this.recalculateAreaSize = this.recalculateAreaSize.bind(this);
+
+    this.state = {
+      resizing: false,
+      placementPercent: 0,
+    };
   }
 
   componentDidMount() {
     window.addEventListener('resize', this.debouncedOnResize);
     document.addEventListener('fullscreenchange', this.onFullscreenChange);
+    window.addEventListener('orientationchange', () => setTimeout(this.recalculateAreaSize, 500));
   }
 
   componentDidUpdate(prevProps) {
-    const { swapLayout, webcamDraggableState } = this.props;
-    const { placement } = webcamDraggableState;
+    const { swapLayout, webcamDraggableState, webcamDraggableDispatch } = this.props;
+    const {
+      placement: statePlacement,
+      orientation,
+      lastPlacementLandscape,
+      lastPlacementPortrait,
+    } = webcamDraggableState;
     const { webcamDraggableState: prevWebcamDraggableState } = prevProps;
-    const { placement: prevPlacement } = prevWebcamDraggableState;
+    const { placement: prevPlacement, orientation: prevOrientation } = prevWebcamDraggableState;
     if (prevProps.swapLayout !== swapLayout) {
       setTimeout(() => this.forceUpdate(), 500);
     }
-
-    if (prevPlacement !== placement) {
+    if (prevPlacement !== statePlacement) {
       setTimeout(() => this.forceUpdate(), 200);
-      setTimeout(() => window.dispatchEvent(new Event('resize')), 400);
+      setTimeout(() => window.dispatchEvent(new Event('resize')), 500);
+    }
+
+    if (prevOrientation !== orientation) {
+      const storagePlacement = Storage.getItem('webcamPlacement');
+      if ((prevOrientation == null || prevOrientation === 'portrait') && orientation === 'landscape') {
+        if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'top') webcamDraggableDispatch({ type: 'setplacementToTop' });
+        if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'bottom') webcamDraggableDispatch({ type: 'setplacementToBottom' });
+      }
+      if ((prevOrientation == null || prevOrientation === 'landscape') && orientation === 'portrait') {
+        if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'left') webcamDraggableDispatch({ type: 'setplacementToLeft' });
+        if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'right') webcamDraggableDispatch({ type: 'setplacementToRight' });
+      }
     }
   }
 
@@ -74,7 +96,11 @@ class WebcamDraggable extends Component {
     this.forceUpdate();
   }
 
-  onResize() {
+  onResizeStart() {
+    this.setState({ resizing: true });
+  }
+
+  onWindowResize() {
     const { webcamDraggableState, webcamDraggableDispatch } = this.props;
     const { mediaSize } = webcamDraggableState;
     const { width: stateWidth, height: stateHeight } = mediaSize;
@@ -90,29 +116,44 @@ class WebcamDraggable extends Component {
           },
         },
       );
-      this.onResizeStop();
     }
+    setTimeout(() => window.dispatchEvent(new Event('resize')), 300);
+  }
+
+  onResize() {
+    this.setPlacementPercent();
   }
 
   onResizeStop() {
     const { webcamDraggableState, webcamDraggableDispatch } = this.props;
-    const { videoListRef } = webcamDraggableState;
-    if (videoListRef) {
-      const videoListRefRect = videoListRef.getBoundingClientRect();
-      const {
-        width, height,
-      } = videoListRefRect;
+    const { optimalGrid } = webcamDraggableState;
+    if (optimalGrid) {
       webcamDraggableDispatch(
         {
           type: 'setVideoListSize',
           value: {
-            width,
-            height,
+            width: optimalGrid.width,
+            height: optimalGrid.height,
           },
         },
       );
     }
+    this.setPlacementPercent();
     window.dispatchEvent(new Event('resize'));
+    setTimeout(() => this.setState({ resizing: false }), 500);
+  }
+
+  setPlacementPercent() {
+    const { webcamDraggableState } = this.props;
+    const { optimalGrid, placement } = webcamDraggableState;
+    if (placement === 'top' || placement === 'bottom') {
+      const mediaHeight = $('section[class^=media]').height();
+      this.setState({ placementPercent: (optimalGrid.height * 100) / mediaHeight });
+    }
+    if (placement === 'left' || placement === 'right') {
+      const mediaWidth = $('section[class^=media]').width();
+      this.setState({ placementPercent: (optimalGrid.width * 100) / mediaWidth });
+    }
   }
 
   getMediaBounds() {
@@ -124,7 +165,6 @@ class WebcamDraggable extends Component {
       const {
         top, left, width: newWidth, height: newHeight,
       } = mediaContainerRect;
-
       if ((mediaState.width === 0 || mediaState.height === 0) && (newWidth > 0 && newHeight > 0)) {
         webcamDraggableDispatch(
           {
@@ -148,7 +188,7 @@ class WebcamDraggable extends Component {
   }
 
   getWebcamsListBounds() {
-    const { webcamDraggableState, singleWebcam } = this.props;
+    const { webcamDraggableState } = this.props;
     const { videoListRef } = webcamDraggableState;
     if (videoListRef) {
       const videoListRefRect = videoListRef.getBoundingClientRect();
@@ -156,15 +196,20 @@ class WebcamDraggable extends Component {
         top, left, width, height,
       } = videoListRefRect;
       return {
-        top: top - 10, // 10 = margin
-        left: left - (singleWebcam ? 10 : 0), // 10 = margin
-        width: width + (singleWebcam ? 20 : 0), // 20 = margin
-        height: height + 20, // 20 = margin
+        top, // 10 = margin
+        left, // 10 = margin
+        width, // 20 = margin
+        height, // 20 = margin
       };
     }
     return false;
   }
 
+  recalculateAreaSize() {
+    this.onResizeStart();
+    this.onResizeStop();
+  }
+
   calculatePosition() {
     const { top: mediaTop, left: mediaLeft } = this.getMediaBounds();
     const { top: webcamsListTop, left: webcamsListLeft } = this.getWebcamsListBounds();
@@ -176,71 +221,76 @@ class WebcamDraggable extends Component {
     };
   }
 
-  async handleWebcamDragStart() {
-    const { webcamDraggableDispatch, singleWebcam } = this.props;
-    const { x, y } = await this.calculatePosition();
-
+  handleWebcamDragStart() {
+    const { webcamDraggableDispatch } = this.props;
+    const { x, y } = this.calculatePosition();
     webcamDraggableDispatch({ type: 'dragStart' });
-
     webcamDraggableDispatch(
       {
         type: 'setTempPosition',
         value: {
-          x: singleWebcam ? x : 0,
+          x,
           y,
         },
       },
     );
   }
 
-  handleWebcamDragStop(e, position) {
-    const { webcamDraggableDispatch, singleWebcam } = this.props;
+  handleWebcamDragStop(e) {
+    const { webcamDraggableDispatch } = this.props;
     const targetClassname = JSON.stringify(e.target.className);
-    const { x, y } = position;
 
     if (targetClassname) {
       if (targetClassname.includes('Top')) {
         webcamDraggableDispatch({ type: 'setplacementToTop' });
+        webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToTop' });
+      } else if (targetClassname.includes('Right')) {
+        webcamDraggableDispatch({ type: 'setplacementToRight' });
+        webcamDraggableDispatch({ type: 'setLastPlacementPortraitToRight' });
       } else if (targetClassname.includes('Bottom')) {
         webcamDraggableDispatch({ type: 'setplacementToBottom' });
-      } else if (singleWebcam) {
-        webcamDraggableDispatch(
-          {
-            type: 'setLastPosition',
-            value: {
-              x,
-              y,
-            },
-          },
-        );
-        webcamDraggableDispatch({ type: 'setplacementToFloating' });
+        webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToBottom' });
+      } else if (targetClassname.includes('Left')) {
+        webcamDraggableDispatch({ type: 'setplacementToLeft' });
+        webcamDraggableDispatch({ type: 'setLastPlacementPortraitToLeft' });
       }
     }
     webcamDraggableDispatch({ type: 'dragEnd' });
     window.dispatchEvent(new Event('resize'));
+    setTimeout(this.recalculateAreaSize, 500);
   }
 
   render() {
     const {
       webcamDraggableState,
-      singleWebcam,
       swapLayout,
       hideOverlay,
       disableVideo,
       audioModalIsOpen,
+      refMediaContainer,
     } = this.props;
 
-    const { dragging, isCameraFullscreen, videoListSize } = webcamDraggableState;
-    let placement = Storage.getItem('webcamPlacement');
+    const {
+      resizing,
+      placementPercent,
+    } = this.state;
+
+    const {
+      dragging,
+      isCameraFullscreen,
+      videoListSize,
+      optimalGrid,
+    } = webcamDraggableState;
+
+    const placement = Storage.getItem('webcamPlacement');
+
     const lastPosition = Storage.getItem('webcamLastPosition') || { x: 0, y: 0 };
+
     let position = lastPosition;
-    if (!placement) {
-      placement = webcamsDefaultPlacement;
-    }
 
     if (dragging) {
       position = webcamDraggableState.tempPosition;
-    } else if (!dragging && placement === 'floating' && singleWebcam) {
+    } else if (!dragging) {
       position = webcamDraggableState.lastPosition;
     } else {
       position = {
@@ -271,30 +321,68 @@ class WebcamDraggable extends Component {
 
     position = {
       x: isOverflowWidth
-        && !dragging && !swapLayout && singleWebcam && placement === 'floating' ? mediaWidth - webcamsWidth : position.x,
+        && !dragging && !swapLayout ? mediaWidth - webcamsWidth : position.x,
       y: isOverflowHeight
-        && !dragging && !swapLayout && singleWebcam && placement === 'floating' ? mediaHeight - (webcamsHeight + 1) : position.y,
+        && !dragging && !swapLayout ? mediaHeight - (webcamsHeight + 1) : position.y,
     };
 
     const contentClassName = cx({
       [styles.content]: true,
-      [styles.fullWidth]: !singleWebcam || swapLayout,
+      [styles.fullWidth]: swapLayout,
+      [styles.fullHeight]: swapLayout,
     });
 
+    const { current: mediaContainer } = refMediaContainer;
+    let layout = 'vertical';
+    if (mediaContainer) {
+      const classNameMediaContainer = mediaContainer.className;
+      if (classNameMediaContainer.includes('containerH')) {
+        layout = 'horizontal';
+      } else {
+        layout = 'vertical';
+      }
+    }
+
     const overlayClassName = cx({
       [styles.overlay]: true,
       [styles.hideOverlay]: hideOverlay,
-      [styles.floatingOverlay]: (singleWebcam && placement === 'floating') || dragging,
-      [styles.autoWidth]: singleWebcam,
-      [styles.fullWidth]: (singleWebcam
-        && (placement === 'top' || placement === 'bottom')
-        && !dragging)
-        || !singleWebcam
-        || swapLayout,
-      [styles.overlayToTop]: (placement === 'floating' && !singleWebcam)
-        || (placement === 'top' && !dragging),
+      [styles.floatingOverlay]: dragging,
+      [styles.autoWidth]: dragging,
+      [styles.fullWidth]: (
+        (
+          placement === 'top'
+          || placement === 'bottom'
+        )
+        || swapLayout
+      )
+        && !dragging,
+      [styles.fullHeight]: (
+        (
+          placement === 'left'
+          && placement === 'right'
+        )
+        || swapLayout
+      )
+        && !dragging,
+      [styles.overlayToTop]: placement === 'top' && !dragging,
+      [styles.overlayToRight]: placement === 'right' && !dragging,
       [styles.overlayToBottom]: placement === 'bottom' && !dragging,
+      [styles.overlayToLeft]: placement === 'left' && !dragging,
       [styles.dragging]: dragging,
+      [styles.hide]: (
+        (
+          placement === 'left'
+          || placement === 'right'
+        )
+        && layout === 'vertical'
+      )
+        || (
+          (
+            placement === 'top'
+            || placement === 'bottom'
+          )
+          && layout === 'horizontal'
+        ),
     });
 
     const dropZoneTopClassName = cx({
@@ -304,6 +392,13 @@ class WebcamDraggable extends Component {
       [styles.cursorGrabbing]: dragging && !isCameraFullscreen,
     });
 
+    const dropZoneLeftClassName = cx({
+      [styles.dropZoneLeft]: true,
+      [styles.show]: dragging,
+      [styles.hide]: !dragging,
+      [styles.cursorGrabbing]: dragging && !isCameraFullscreen,
+    });
+
     const dropZoneBottomClassName = cx({
       [styles.dropZoneBottom]: true,
       [styles.show]: dragging,
@@ -311,25 +406,78 @@ class WebcamDraggable extends Component {
       [styles.cursorGrabbing]: dragging && !isCameraFullscreen,
     });
 
+    const dropZoneRightClassName = cx({
+      [styles.dropZoneRight]: true,
+      [styles.show]: dragging,
+      [styles.hide]: !dragging,
+      [styles.cursorGrabbing]: dragging && !isCameraFullscreen,
+    });
+
     const dropZoneBgTopClassName = cx({
       [styles.dropZoneBgTop]: true,
     });
 
+    const dropZoneBgLeftClassName = cx({
+      [styles.dropZoneBgLeft]: true,
+    });
+
     const dropZoneBgBottomClassName = cx({
       [styles.dropZoneBgBottom]: true,
     });
 
+    const dropZoneBgRightClassName = cx({
+      [styles.dropZoneBgRight]: true,
+    });
+
+    const mHeight = $('section[class^=media]').height();
+    const mWidth = $('section[class^=media]').width();
+
+    let resizeWidth;
+    let resizeHeight;
+    if (resizing && (placement === 'top' || placement === 'bottom') && !dragging) {
+      resizeWidth = '100%';
+      resizeHeight = videoListSize.height;
+    }
+    if (!resizing && (placement === 'top' || placement === 'bottom') && !dragging) {
+      resizeWidth = '100%';
+      resizeHeight = mHeight * (placementPercent / 100);
+    }
+
+    if (resizing && (placement === 'left' || placement === 'right') && !dragging) {
+      resizeWidth = videoListSize.width;
+      resizeHeight = '100%';
+    }
+    if (!resizing && (placement === 'left' || placement === 'right') && !dragging) {
+      resizeWidth = mWidth * (placementPercent / 100);
+      resizeHeight = '100%';
+    }
+
+    if (dragging) {
+      resizeHeight = optimalGrid.height;
+      resizeWidth = optimalGrid.width;
+    }
+
     return (
       <Fragment>
         <div
           className={dropZoneTopClassName}
-          style={{ height: !singleWebcam ? '50%' : '20%' }}
+          style={{ height: '15vh' }}
         >
           <div
             className={dropZoneBgTopClassName}
           />
         </div>
-
+        <div
+          className={dropZoneLeftClassName}
+          style={{
+            width: '15vh',
+            height: `calc(${mediaHeight}px - (15vh * 2))`,
+          }}
+        >
+          <div
+            className={dropZoneBgLeftClassName}
+          />
+        </div>
         <Draggable
           handle="video"
           bounds="#container"
@@ -341,24 +489,21 @@ class WebcamDraggable extends Component {
         >
           <Resizable
             size={
-              singleWebcam
-                ? {
-                  height: videoListSize.height,
-                  width: videoListSize.width,
-                }
-                : {
-                  height: videoListSize.height,
-                }
+              {
+                height: resizeHeight,
+                width: resizeWidth,
+              }
             }
             lockAspectRatio
             handleWrapperClass="resizeWrapper"
+            onResizeStart={this.onResizeStart}
             onResize={dispatchResizeEvent}
             onResizeStop={this.onResizeStop}
             enable={{
-              top: !(placement === 'top') && !swapLayout,
-              bottom: !(placement === 'bottom') && !swapLayout,
-              left: false,
-              right: false,
+              top: (placement === 'bottom') && !swapLayout,
+              bottom: (placement === 'top') && !swapLayout,
+              left: (placement === 'right') && !swapLayout,
+              right: (placement === 'left') && !swapLayout,
               topLeft: false,
               topRight: false,
               bottomLeft: false,
@@ -369,14 +514,8 @@ class WebcamDraggable extends Component {
                 ? overlayClassName
                 : contentClassName}
             style={{
-              marginLeft: singleWebcam
-                && !(placement === 'bottom' || placement === 'top')
-                ? 10
-                : 0,
-              marginRight: singleWebcam
-                && !(placement === 'bottom' || placement === 'top')
-                ? 10
-                : 0,
+              marginLeft: 0,
+              marginRight: 0,
             }}
           >
             {
@@ -391,15 +530,25 @@ class WebcamDraggable extends Component {
             }
           </Resizable>
         </Draggable>
-
         <div
           className={dropZoneBottomClassName}
-          style={{ height: !singleWebcam ? '50%' : '20%' }}
+          style={{ height: '15vh' }}
         >
           <div
             className={dropZoneBgBottomClassName}
           />
         </div>
+        <div
+          className={dropZoneRightClassName}
+          style={{
+            width: '15vh',
+            height: `calc(${mediaHeight}px - (15vh * 2))`,
+          }}
+        >
+          <div
+            className={dropZoneBgRightClassName}
+          />
+        </div>
       </Fragment>
     );
   }
@@ -408,4 +557,4 @@ class WebcamDraggable extends Component {
 WebcamDraggable.propTypes = propTypes;
 WebcamDraggable.defaultProps = defaultProps;
 
-export default withDraggableContext(WebcamDraggable);
+export default withDraggableConsumer(WebcamDraggable);
diff --git a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx
index bda0d4c6d87e786efc3470b260aa76c5ba392f10..155886dcaff28bbc87de56abe02943123bc9fb3d 100644
--- a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx
+++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx
@@ -1,10 +1,15 @@
 import React, { createContext, useReducer, useEffect } from 'react';
 import Storage from '../../../services/storage/session';
 
+const { webcamsDefaultPlacement } = Meteor.settings.public.layout;
+
 export const WebcamDraggableContext = createContext();
 
 const initialState = {
-  placement: 'top',
+  placement: webcamsDefaultPlacement,
+  lastPlacementLandscape: 'top',
+  lastPlacementPortrait: 'left',
+  orientation: null,
   mediaSize: {
     width: 0,
     height: 0,
@@ -25,6 +30,7 @@ const initialState = {
     x: 0,
     y: 0,
   },
+  optimalGrid: {},
   dragging: false,
   videoRef: null,
   videoListRef: null,
@@ -39,18 +45,66 @@ const reducer = (state, action) => {
         placement: 'top',
       };
     }
+    case 'setplacementToRight': {
+      return {
+        ...state,
+        placement: 'right',
+      };
+    }
     case 'setplacementToBottom': {
       return {
         ...state,
         placement: 'bottom',
       };
     }
+    case 'setplacementToLeft': {
+      return {
+        ...state,
+        placement: 'left',
+      };
+    }
+    case 'setLastPlacementPortraitToLeft': {
+      return {
+        ...state,
+        lastPlacementPortrait: 'left',
+      };
+    }
+    case 'setLastPlacementPortraitToRight': {
+      return {
+        ...state,
+        lastPlacementPortrait: 'right',
+      };
+    }
+    case 'setLastPlacementLandscapeToTop': {
+      return {
+        ...state,
+        lastPlacementLandscape: 'top',
+      };
+    }
+    case 'setLastPlacementLandscapeToBottom': {
+      return {
+        ...state,
+        lastPlacementLandscape: 'bottom',
+      };
+    }
     case 'setplacementToFloating': {
       return {
         ...state,
         placement: 'floating',
       };
     }
+    case 'setOrientationToLandscape': {
+      return {
+        ...state,
+        orientation: 'landscape',
+      };
+    }
+    case 'setOrientationToPortrait': {
+      return {
+        ...state,
+        orientation: 'portrait',
+      };
+    }
     case 'setMediaSize': {
       return {
         ...state,
@@ -114,6 +168,12 @@ const reducer = (state, action) => {
         videoListRef: action.value,
       };
     }
+    case 'setOptimalGrid': {
+      return {
+        ...state,
+        optimalGrid: action.value,
+      };
+    }
     case 'dragStart': {
       return {
         ...state,
@@ -146,13 +206,22 @@ const ContextConsumer = Component => props => (
 
 const ContextProvider = (props) => {
   const [webcamDraggableState, webcamDraggableDispatch] = useReducer(reducer, initialState);
-  const { placement, lastPosition } = webcamDraggableState;
+  const {
+    placement,
+    lastPosition,
+    lastPlacementLandscape,
+    lastPlacementPortrait,
+  } = webcamDraggableState;
   const { children } = props;
   useEffect(() => {
     Storage.setItem('webcamPlacement', placement);
+    Storage.setItem('webcamLastPlacementLandscape', lastPlacementLandscape);
+    Storage.setItem('webcamlastPlacementPortrait', lastPlacementPortrait);
     Storage.setItem('webcamLastPosition', lastPosition);
   }, [
     placement,
+    lastPlacementLandscape,
+    lastPlacementPortrait,
     lastPosition,
   ]);
 
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
index 0b6b1a6f6876fa5bf199f82d6c694063bd308e65..d736d3b4efd63b44b0d4cf4526081489565a49cf 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
@@ -15,6 +15,7 @@ import PresentationCloseButton from './presentation-close-button/component';
 import DownloadPresentationButton from './download-presentation-button/component';
 import FullscreenService from '../fullscreen-button/service';
 import FullscreenButtonContainer from '../fullscreen-button/container';
+import { withDraggableContext, withDraggableConsumer } from '../media/webcam-draggable-overlay/context';
 
 const intlMessages = defineMessages({
   presentationLabel: {
@@ -81,10 +82,25 @@ class PresentationArea extends PureComponent {
     window.addEventListener('resize', this.onResize);
     this.getInitialPresentationSizes();
     this.refPresentationContainer.addEventListener('fullscreenchange', this.onFullscreenChange);
+
+    const { slidePosition, webcamDraggableDispatch } = this.props;
+    const { width: currWidth, height: currHeight } = slidePosition;
+    if (currWidth > currHeight || currWidth === currHeight) {
+      webcamDraggableDispatch({ type: 'setOrientationToLandscape' });
+    }
+    if (currHeight > currWidth) {
+      webcamDraggableDispatch({ type: 'setOrientationToPortrait' });
+    }
   }
 
   componentDidUpdate(prevProps) {
-    const { currentPresentation, notify, intl } = this.props;
+    const {
+      currentPresentation,
+      notify,
+      intl,
+      slidePosition,
+      webcamDraggableDispatch,
+    } = this.props;
 
     if (prevProps.currentPresentation.name !== currentPresentation.name) {
       notify(
@@ -93,6 +109,18 @@ class PresentationArea extends PureComponent {
         'presentation',
       );
     }
+
+    const { width: prevWidth, height: prevHeight } = prevProps.slidePosition;
+    const { width: currWidth, height: currHeight } = slidePosition;
+
+    if (prevWidth !== currWidth || prevHeight !== currHeight) {
+      if (currWidth > currHeight || currWidth === currHeight) {
+        webcamDraggableDispatch({ type: 'setOrientationToLandscape' });
+      }
+      if (currHeight > currWidth) {
+        webcamDraggableDispatch({ type: 'setOrientationToPortrait' });
+      }
+    }
   }
 
   componentWillUnmount() {
@@ -654,7 +682,7 @@ class PresentationArea extends PureComponent {
   }
 }
 
-export default injectIntl(PresentationArea);
+export default injectIntl(withDraggableConsumer(PresentationArea));
 
 PresentationArea.propTypes = {
   intl: intlShape.isRequired,
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
index 471ab9c099c518774760b7279706dbb268b3fbd9..bfe4b551bc8d658fd1605b061c08e6699594c0d1 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
@@ -111,7 +111,7 @@ class VideoList extends Component {
   }
 
   setOptimalGrid() {
-    const { streams } = this.props;
+    const { streams, webcamDraggableDispatch } = this.props;
     let numItems = streams.length;
     if (numItems < 1 || !this.canvas || !this.grid) {
       return;
@@ -137,6 +137,12 @@ class VideoList extends Component {
         const betterThanCurrent = testGrid.filledArea > currentGrid.filledArea;
         return focusedConstraint && betterThanCurrent ? testGrid : currentGrid;
       }, { filledArea: 0 });
+    webcamDraggableDispatch(
+      {
+        type: 'setOptimalGrid',
+        value: optimalGrid,
+      },
+    );
     this.setState({
       optimalGrid,
     });
diff --git a/bigbluebutton-html5/package.json b/bigbluebutton-html5/package.json
index 757b01f7bb981d98fe9d219ac61954e1272719ea..33f5efbb51a73015d536e39357e959a42ea3d68d 100755
--- a/bigbluebutton-html5/package.json
+++ b/bigbluebutton-html5/package.json
@@ -57,6 +57,7 @@
     "re-resizable": "^4.11.0",
     "react": "^16.12.0",
     "react-autosize-textarea": "^5.0.1",
+    "react-device-detect": "^1.11.14",
     "react-color": "^2.18.0",
     "react-dom": "^16.12.0",
     "react-draggable": "^3.3.2",