diff --git a/front/dist/resources/html/gameReport.html b/front/dist/resources/html/gameReport.html
index 9a761c328816227103052649a867786db391a63d..59ca35920de83385533e6e2820bf37f620aee825 100644
--- a/front/dist/resources/html/gameReport.html
+++ b/front/dist/resources/html/gameReport.html
@@ -12,10 +12,6 @@
         border-radius: 6px;
         margin: 2px auto 0;
         width: 298px;
-        height: 220px;
-    }
-    #gameReport .cautiousText {
-        font-size: 50%;
     }
     #gameReport h1 {
         background-image: linear-gradient(top, #f1f3f3, #d4dae0);
@@ -30,6 +26,9 @@
         text-align: center;
         text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
     }
+    #gameReport h3 {
+        margin: 0;
+    }
     #gameReport textarea {
         font-size: 70%;
         background: linear-gradient(top, #d6d7d7, #dee0e0);
@@ -51,15 +50,17 @@
     }
     #gameReport button {
         margin-top: 10px;
-        background-color: black;
+        font-size: 60%;
+        background-color: #dc3545;
         color: white;
         border-radius: 7px;
-        padding-bottom: 4px;
-        width: 60px;
+        padding: 3px 10px 3px 10px;
     }
     #gameReport button#gameReportFormCancel {
         background-color: #c7c7c700;
         color: #292929;
+        display: block; 
+        float: right;
     }
     #gameReport section a{
         text-align: center;
@@ -74,8 +75,11 @@
     #gameReport section.text-center{
         text-align: center;
     }
-    #gameReport section p{
+    #gameReport p{
         font-size: 8px;
+        margin: 3px 0 0 0;
+    }
+    #gameReport form p{
         margin: 0px 70px;
     }
     #gameReport section p.err{
@@ -87,18 +91,32 @@
     }
 </style>
 
-<form id="gameReport" hidden>
-    <section class="text-center">
-        <h5 id="nameReported"></h5>
-        <input type="hidden" id="idUserReported"/>
+<main id="gameReport" hidden>
+    <section>
+        <button id="gameReportFormCancel">X</button>
+        <h1>Moderate <span id="nameReported"></span></h1>
+        <p id="askActionP">What action do you want to take?</p>
     </section>
     <section>
-        <h6>Message</h6>
-        <textarea type="text" name="report" id="gameReportInput"></textarea>
-        <p class="err" id="gameReportErr"></p>
+        <h3>Block: </h3>
+        <p>Block any communication from and to this user. This can be reverted.</p>
+        <section class="action">
+            <button id="toggleBlockButton">Block this user</button>
+        </section>
     </section>
-    <section class="action">
-        <button type="submit" id="gameReportFormSubmit">Submit</button>
-        <button type="submit" id="gameReportFormCancel">Close</button>
+    <section id="reportSection">
+        <h3>Report: </h3>
+        <p>Send a report message to the administrators of this room. They may later ban this user.</p>
+        <form>
+            <section>
+                <h6>Your message: </h6>
+                <textarea type="text" name="report" id="gameReportInput"></textarea>
+                <p class="err" id="gameReportErr"></p>
+            </section>
+            <section class="action">
+                <button type="submit" id="gameReportFormSubmit">Report this user</button>
+            </section>
+        </form>
     </section>
-</form>
+</main>
+
diff --git a/front/dist/resources/html/gameShare.html b/front/dist/resources/html/gameShare.html
index 4e487328d00081c2f41e0ce450ed5de8ac754e54..21c65014cf434bc580a0a009072aaf99d604f6f9 100644
--- a/front/dist/resources/html/gameShare.html
+++ b/front/dist/resources/html/gameShare.html
@@ -14,9 +14,6 @@
         width: 298px;
         height: 150px;
     }
-    #gameShare .cautiousText {
-        font-size: 50%;
-    }
     #gameShare h1 {
         background-image: linear-gradient(top, #f1f3f3, #d4dae0);
         border-bottom: 1px solid #a6abaf;
diff --git a/front/dist/resources/logos/blockSign.svg b/front/dist/resources/logos/blockSign.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c64ba2949a211e551fc991681dc8df528379a09d
--- /dev/null
+++ b/front/dist/resources/logos/blockSign.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg2985" version="1.1" inkscape:version="0.48.4 r9939" width="485.33627" height="485.33627" sodipodi:docname="600px-France_road_sign_B1j.svg[1].png">
+  <metadata id="metadata2991">
+    <rdf:RDF>
+      <cc:Work rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+        <dc:title/>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs id="defs2989"/>
+  <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1272" inkscape:window-height="745" id="namedview2987" showgrid="false" inkscape:snap-global="true" inkscape:snap-grids="true" inkscape:snap-bbox="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:object-paths="true" inkscape:snap-intersection-paths="true" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-center="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:zoom="0.59970176" inkscape:cx="390.56499" inkscape:cy="244.34365" inkscape:window-x="86" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="layer1">
+    <inkscape:grid type="xygrid" id="grid2995" empspacing="5" visible="true" enabled="true" snapvisiblegridlinesonly="true" originx="-57.33186px" originy="-57.33186px"/>
+  </sodipodi:namedview>
+  <g inkscape:groupmode="layer" id="layer1" inkscape:label="1" style="display:inline" transform="translate(-57.33186,-57.33186)">
+    <path sodipodi:type="arc" style="color:#000000;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" id="path2997" sodipodi:cx="300" sodipodi:cy="300" sodipodi:rx="240" sodipodi:ry="240" d="M 540,300 C 540,432.54834 432.54834,540 300,540 167.45166,540 60,432.54834 60,300 60,167.45166 167.45166,60 300,60 432.54834,60 540,167.45166 540,300 z" transform="matrix(1.0058783,0,0,1.0058783,-1.76349,-1.76349)"/>
+    <path sodipodi:type="arc" style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" id="path4005" sodipodi:cx="304.75" sodipodi:cy="214.75" sodipodi:rx="44.75" sodipodi:ry="44.75" d="m 349.5,214.75 c 0,24.71474 -20.03526,44.75 -44.75,44.75 -24.71474,0 -44.75,-20.03526 -44.75,-44.75 0,-24.71474 20.03526,-44.75 44.75,-44.75 24.71474,0 44.75,20.03526 44.75,44.75 z" transform="matrix(5.1364411,0,0,5.1364411,-1265.3304,-803.05073)"/>
+    <rect style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" id="rect4001" width="345" height="80.599998" x="127.5" y="259.70001"/>
+  </g>
+</svg>
\ No newline at end of file
diff --git a/front/dist/resources/logos/blockingIcon.png b/front/dist/resources/logos/blockingIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef5f66cc49e59d3883292a056d1f167652a0e7d8
Binary files /dev/null and b/front/dist/resources/logos/blockingIcon.png differ
diff --git a/front/dist/resources/logos/cancel.png b/front/dist/resources/logos/cancel.png
new file mode 100644
index 0000000000000000000000000000000000000000..5bf9b6d2914a24bf0b36fb52cefd3380cb3434bb
Binary files /dev/null and b/front/dist/resources/logos/cancel.png differ
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css
index 111700d1e80292d7280bf407e4592e08864a8dc1..4bf05455f1e120ef95ca72771c6d8a4b6152a091 100644
--- a/front/dist/resources/style/style.css
+++ b/front/dist/resources/style/style.css
@@ -65,6 +65,12 @@ body .message-info.warning{
     padding: 10px;
     z-index: 2;
 }
+.video-container img.block-logo {
+    left: 30%;
+    bottom: 15%;
+    width: 150px;
+    height: 150px;
+}
 
 .video-container button.report{
     display: block;
@@ -91,7 +97,7 @@ body .message-info.warning{
 }
 
 .video-container button.report:hover {
-    width: 94px;
+    width: 150px;
 }
 
 .video-container button.report img{
@@ -111,6 +117,9 @@ body .message-info.warning{
     font-size: 16px;
     cursor: url('/resources/logos/cursor_pointer.png'), pointer;
 }
+.video-container img.active {
+    display: block !important;
+}
 
 .video-container video{
     height: 100%;
@@ -188,10 +197,7 @@ video#myCamVideo{
     transition: all .2s;
     right: 224px;
 }
-/*.btn-call{
-    transition: all .1s;
-    left: 0px;
-}*/
+
 .btn-cam-action div img{
     height: 22px;
     width: 30px;
diff --git a/front/package.json b/front/package.json
index 41932ca9c0f6ac786581d96ec5f6983e1f60fc1d..0e50ba805d1f000d3932a4a9e1aa73dfdce8a414 100644
--- a/front/package.json
+++ b/front/package.json
@@ -29,6 +29,7 @@
     "phaser": "3.24.1",
     "queue-typescript": "^1.0.1",
     "quill": "^1.3.7",
+    "rxjs": "^6.6.3",
     "simple-peer": "^9.6.2",
     "socket.io-client": "^2.3.0",
     "webpack-require-http": "^0.4.3"
diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts
index bd330ad97c7aac6ba19f31ffaec0f2fafb7dfe24..8eb7462fcbfc30434985347c3cfe1a6e294ffc18 100644
--- a/front/src/Connexion/RoomConnection.ts
+++ b/front/src/Connexion/RoomConnection.ts
@@ -464,7 +464,8 @@ export class RoomConnection implements RoomConnection {
         });
     }
 
-    public getUserId(): number|null {
+    public getUserId(): number {
+        if (this.userId === null) throw 'UserId cannot be null!'
         return this.userId;
     }
 
diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts
index 822e4e3664629dc0c54e7226705fb6e48f7ec9c7..81ea00e1e358bf346ec3cf30a498032aafd95ac9 100644
--- a/front/src/Phaser/Entity/RemotePlayer.ts
+++ b/front/src/Phaser/Entity/RemotePlayer.ts
@@ -1,7 +1,6 @@
 import {GameScene} from "../Game/GameScene";
 import {PointInterface} from "../../Connexion/ConnexionModels";
 import {Character} from "../Entity/Character";
-import {Sprite} from "./Sprite";
 
 /**
  * Class representing the sprite of a remote player (a player that plays on another computer)
@@ -23,6 +22,11 @@ export class RemotePlayer extends Character {
 
         //set data
         this.userId = userId;
+        
+        //todo: implement on click action
+        /*this.playerName.setInteractive();
+        this.playerName.on('pointerup', () => {
+        });*/
     }
 
     updatePosition(position: PointInterface): void {
diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts
index acc1ec3d2d81dbbdcd2e2126f1a7a47a768ac215..ab25c338fb0c314593103f0b4255f3e6ec1df32f 100644
--- a/front/src/Phaser/Menu/MenuScene.ts
+++ b/front/src/Phaser/Menu/MenuScene.ts
@@ -2,17 +2,16 @@ import {LoginScene, LoginSceneName} from "../Login/LoginScene";
 import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene";
 import {gameManager} from "../Game/GameManager";
 import {localUserStore} from "../../Connexion/LocalUserStore";
-import {mediaManager, ReportCallback, ShowReportCallBack} from "../../WebRtc/MediaManager";
-import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
-import {GameConnexionTypes} from "../../Url/UrlManager";
+import {mediaManager} from "../../WebRtc/MediaManager";
+import {gameReportKey, gameReportRessource, ReportMenu} from "./ReportMenu";
 import {connectionManager} from "../../Connexion/ConnectionManager";
+import {GameConnexionTypes} from "../../Url/UrlManager";
 
 export const MenuSceneName = 'MenuScene';
 const gameMenuKey = 'gameMenu';
 const gameMenuIconKey = 'gameMenuIcon';
 const gameSettingsMenuKey = 'gameSettingsMenu';
 const gameShare = 'gameShare';
-const gameReport = 'gameReport';
 
 const closedSideMenuX = -200;
 const openedSideMenuX = 0;
@@ -24,11 +23,10 @@ export class MenuScene extends Phaser.Scene {
     private menuElement!: Phaser.GameObjects.DOMElement;
     private gameQualityMenuElement!: Phaser.GameObjects.DOMElement;
     private gameShareElement!: Phaser.GameObjects.DOMElement;
-    private gameReportElement!: Phaser.GameObjects.DOMElement;
+    private gameReportElement!: ReportMenu;
     private sideMenuOpened = false;
     private settingsMenuOpened = false;
     private gameShareOpened = false;
-    private gameReportOpened = false;
     private gameQualityValue: number;
     private videoQualityValue: number;
     private menuButton!: Phaser.GameObjects.DOMElement;
@@ -45,21 +43,21 @@ export class MenuScene extends Phaser.Scene {
         this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html');
         this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html');
         this.load.html(gameShare, 'resources/html/gameShare.html');
-        this.load.html(gameReport, 'resources/html/gameReport.html');
+        this.load.html(gameReportKey, gameReportRessource);
     }
 
     create() {
         this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey);
         this.menuElement.setOrigin(0);
-        this.revealMenusAfterInit(this.menuElement, 'gameMenu');
+        MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu');
 
         const middleX = (window.innerWidth / 3) - 298;
         this.gameQualityMenuElement = this.add.dom(middleX, -400).createFromCache(gameSettingsMenuKey);
-        this.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality');
+        MenuScene.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality');
 
 
         this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare);
-        this.revealMenusAfterInit(this.gameShareElement, gameShare);
+        MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare);
         this.gameShareElement.addListener('click');
         this.gameShareElement.on('click',  (event:MouseEvent) => {
             event.preventDefault();
@@ -70,18 +68,11 @@ export class MenuScene extends Phaser.Scene {
             }
         });
 
-        this.gameReportElement = this.add.dom(middleX, -400).createFromCache(gameReport);
-        this.revealMenusAfterInit(this.gameReportElement, gameReport);
-        this.gameReportElement.addListener('click');
-        this.gameReportElement.on('click',  (event:MouseEvent) => {
-            event.preventDefault();
-            if((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') {
-                this.submitReport();
-            }else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') {
-                this.closeGameReport();
-            }
+        this.gameReportElement = new ReportMenu(this, connectionManager.getConnexionType === GameConnexionTypes.anonymous);
+        mediaManager.setShowReportModalCallBacks((userId, userName) => {
+            this.closeAll();
+            this.gameReportElement.open(parseInt(userId), userName);
         });
-        mediaManager.setShowReportModalCallBacks(this.openGameReport.bind(this));
 
         this.input.keyboard.on('keyup-TAB', () => {
             this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
@@ -96,7 +87,8 @@ export class MenuScene extends Phaser.Scene {
         this.menuElement.on('click', this.onMenuClick.bind(this));
     }
 
-    private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
+    //todo put this method in a parent menuElement class
+    static revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
         //Dom elements will appear inside the viewer screen when creating before being moved out of it, which create a flicker effect.
         //To prevent this, we put a 'hidden' attribute on the root element, we remove it only after the init is done.
         setTimeout(() => {
@@ -245,71 +237,6 @@ export class MenuScene extends Phaser.Scene {
         });
     }
 
-    private openGameReport(userId: string, userName: string|undefined){
-        if (this.gameReportOpened) {
-            this.closeGameReport();
-            return;
-        }
-
-        //close all
-        this.closeAll();
-
-        const gameTitleReport = this.gameReportElement.getChildByID('nameReported') as HTMLElement;
-        gameTitleReport.innerText = userName ? `Report user: ${userName}` : 'Report user';
-        const gameIdUserReported = this.gameReportElement.getChildByID('idUserReported') as HTMLInputElement;
-        gameIdUserReported.value = userId;
-
-        this.gameReportOpened = true;
-        let middleY = (window.innerHeight / 3) - (257);
-        if(middleY < 0){
-            middleY = 0;
-        }
-        let middleX = (window.innerWidth / 3) - 298;
-        if(middleX < 0){
-            middleX = 0;
-        }
-
-        gameManager.getCurrentGameScene(this).userInputManager.clearAllKeys();
-
-        this.tweens.add({
-            targets: this.gameReportElement,
-            y: middleY,
-            x: middleX,
-            duration: 1000,
-            ease: 'Power3'
-        });
-        return;
-    }
-
-    private closeGameReport(): void{
-        this.gameReportOpened = false;
-        gameManager.getCurrentGameScene(this).userInputManager.initKeyBoardEvent();
-        this.tweens.add({
-            targets: this.gameReportElement,
-            y: -400,
-            duration: 1000,
-            ease: 'Power3'
-        });
-    }
-
-    private submitReport(): void{
-        const gamePError = this.gameReportElement.getChildByID('gameReportErr') as HTMLParagraphElement;
-        gamePError.innerText = '';
-        gamePError.style.display = 'none';
-        const gameTextArea = this.gameReportElement.getChildByID('gameReportInput') as HTMLInputElement;
-        const gameIdUserReported = this.gameReportElement.getChildByID('idUserReported') as HTMLInputElement;
-        if(!gameTextArea || !gameTextArea.value || !gameIdUserReported || !gameIdUserReported.value){
-            gamePError.innerText = 'Report message cannot to be empty.';
-            gamePError.style.display = 'block';
-            return;
-        }
-        gameManager.getCurrentGameScene(this).connection.emitReportPlayerMessage(
-            parseInt(gameIdUserReported.value),
-            gameTextArea.value
-        );
-        this.closeGameReport();
-    }
-
     private onMenuClick(event:MouseEvent) {
         if((event?.target as HTMLInputElement).classList.contains('not-button')){
             return;
@@ -372,6 +299,6 @@ export class MenuScene extends Phaser.Scene {
     private closeAll(){
         this.closeGameQualityMenu();
         this.closeGameShare();
-        this.closeGameReport();
+        this.gameReportElement.close();
     }
 }
diff --git a/front/src/Phaser/Menu/ReportMenu.ts b/front/src/Phaser/Menu/ReportMenu.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bee86c35ed5c05a96d21bee96b2a4a6386bd3686
--- /dev/null
+++ b/front/src/Phaser/Menu/ReportMenu.ts
@@ -0,0 +1,119 @@
+import {MenuScene} from "./MenuScene";
+import {gameManager} from "../Game/GameManager";
+import {blackListManager} from "../../WebRtc/BlackListManager";
+
+export const gameReportKey = 'gameReport';
+export const gameReportRessource = 'resources/html/gameReport.html';
+
+export class ReportMenu extends Phaser.GameObjects.DOMElement {
+    private opened: boolean = false;
+    
+    private userId!: number;
+    private userName!: string|undefined;
+    private anonymous: boolean;
+    
+    constructor(scene: Phaser.Scene, anonymous: boolean) {
+        super(scene, -2000, -2000);
+        this.anonymous = anonymous;
+        this.createFromCache(gameReportKey);
+
+        if (this.anonymous) {
+            const divToHide = this.getChildByID('reportSection') as HTMLElement;
+            divToHide.hidden = true;
+            const textToHide = this.getChildByID('askActionP') as HTMLElement;
+            textToHide.hidden = true;
+        }
+        
+        scene.add.existing(this);
+        MenuScene.revealMenusAfterInit(this, gameReportKey);
+
+        this.addListener('click');
+        this.on('click',  (event:MouseEvent) => {
+            event.preventDefault();
+            if ((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') {
+                this.submitReport();
+            } else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') {
+                this.close();
+            } else if((event?.target as HTMLInputElement).id === 'toggleBlockButton') {
+                this.toggleBlock();
+            }
+        });
+    }
+
+    public open(userId: number, userName: string|undefined): void {
+        if (this.opened) {
+            this.close();
+            return;
+        }
+        
+        this.userId = userId;
+        this.userName = userName;
+        
+        const mainEl = this.getChildByID('gameReport') as HTMLElement;
+        this.x = this.getCenteredX(mainEl);
+        this.y = this.getHiddenY(mainEl);
+
+        const gameTitleReport = this.getChildByID('nameReported') as HTMLElement;
+        gameTitleReport.innerText = userName || '';
+
+        const blockButton = this.getChildByID('toggleBlockButton') as HTMLElement;
+        blockButton.innerText = blackListManager.isBlackListed(this.userId) ? 'Unblock this user' : 'Block this user';
+
+        this.opened = true;
+
+        gameManager.getCurrentGameScene(this.scene).userInputManager.clearAllKeys();
+
+        this.scene.tweens.add({
+            targets: this,
+            y: this.getCenteredY(mainEl),
+            duration: 1000,
+            ease: 'Power3'
+        });
+    }
+
+    public close(): void {
+        this.opened = false;
+        gameManager.getCurrentGameScene(this.scene).userInputManager.initKeyBoardEvent();
+        const mainEl = this.getChildByID('gameReport') as HTMLElement;
+        this.scene.tweens.add({
+            targets: this,
+            y: this.getHiddenY(mainEl),
+            duration: 1000,
+            ease: 'Power3'
+        });
+    }
+    
+    //todo: into a parent class?
+    private getCenteredX(mainEl: HTMLElement): number {
+        return window.innerWidth / 4 - mainEl.clientWidth / 2;
+    }
+    private getHiddenY(mainEl: HTMLElement): number {
+        return - mainEl.clientHeight - 50;
+    }
+    private getCenteredY(mainEl: HTMLElement): number {
+        return window.innerHeight / 4 - mainEl.clientHeight / 2;
+    }
+    
+    private toggleBlock(): void {
+        !blackListManager.isBlackListed(this.userId) ? blackListManager.blackList(this.userId) : blackListManager.cancelBlackList(this.userId);
+        this.close();
+    }
+
+    private submitReport(): void{
+        const gamePError = this.getChildByID('gameReportErr') as HTMLParagraphElement;
+        gamePError.innerText = '';
+        gamePError.style.display = 'none';
+        const gameTextArea = this.getChildByID('gameReportInput') as HTMLInputElement;
+        const gameIdUserReported = this.getChildByID('idUserReported') as HTMLInputElement;
+        if(!gameTextArea || !gameTextArea.value || !gameIdUserReported || !gameIdUserReported.value){
+            gamePError.innerText = 'Report message cannot to be empty.';
+            gamePError.style.display = 'block';
+            return;
+        }
+        gameManager.getCurrentGameScene(this.scene).connection.emitReportPlayerMessage(
+            parseInt(gameIdUserReported.value),
+            gameTextArea.value
+        );
+        this.close();
+    }
+}
\ No newline at end of file
diff --git a/front/src/WebRtc/BlackListManager.ts b/front/src/WebRtc/BlackListManager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65efef3aa1880c863ba3db2d0c61954cd13baf34
--- /dev/null
+++ b/front/src/WebRtc/BlackListManager.ts
@@ -0,0 +1,24 @@
+import {Subject} from 'rxjs';
+
+class BlackListManager {
+    private list: number[] = [];
+    public onBlockStream: Subject<number> = new Subject();
+    public onUnBlockStream: Subject<number> = new Subject();
+    
+    isBlackListed(userId: number): boolean {
+        return this.list.find((data) => data === userId) !== undefined;
+    }
+    
+    blackList(userId: number): void {
+        if (this.isBlackListed(userId)) return;
+        this.list.push(userId);
+        this.onBlockStream.next(userId);
+    }
+
+    cancelBlackList(userId: number): void {
+        this.list.splice(this.list.findIndex(data => data === userId), 1);
+        this.onUnBlockStream.next(userId);
+    }
+}
+
+export const blackListManager = new BlackListManager();
\ No newline at end of file
diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts
index 7874949192d9c2a87353352c828558eedf711dee..12a6f7060cf160f50f7b8991a8093157287cd77a 100644
--- a/front/src/WebRtc/MediaManager.ts
+++ b/front/src/WebRtc/MediaManager.ts
@@ -3,8 +3,7 @@ import {HtmlUtils} from "./HtmlUtils";
 import {discussionManager, SendMessageCallback} from "./DiscussionManager";
 import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
 import {VIDEO_QUALITY_SELECT} from "../Administration/ConsoleGlobalMessageManager";
-import {connectionManager} from "../Connexion/ConnectionManager";
-import {GameConnexionTypes} from "../Url/UrlManager";
+import {UserSimplePeerInterface} from "./SimplePeer";
 declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
 
 const localValueVideo = localStorage.getItem(VIDEO_QUALITY_SELECT);
@@ -28,7 +27,6 @@ export type ReportCallback = (message: string) => void;
 export type ShowReportCallBack = (userId: string, userName: string|undefined) => void;
 
 // TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
-// TODO: verify that microphone event listeners are not triggered plenty of time NOW (since MediaManager is created many times!!!!)
 export class MediaManager {
     localStream: MediaStream|null = null;
     localScreenCapture: MediaStream|null = null;
@@ -473,8 +471,9 @@ export class MediaManager {
         return this.getCamera();
     }
 
-    addActiveVideo(userId: string, userName: string = "", anonymous: boolean = true){
+    addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){
         this.webrtcInAudio.play();
+        const userId = ''+user.userId
 
         userName = userName.toUpperCase();
         const color = this.getColorByString(userName);
@@ -484,22 +483,18 @@ export class MediaManager {
                 <div class="connecting-spinner"></div>
                 <div class="rtc-error" style="display: none"></div>
                 <i id="name-${userId}" style="background-color: ${color};">${userName}</i>
-                <img id="microphone-${userId}" src="resources/logos/microphone-close.svg">
-                ` +
-                ((anonymous === false)?`
-                    <button id="report-${userId}" class="report">
-                        <img src="resources/logos/report.svg">
-                        <span>Report</span>
-                    </button>
-                `:''
-                )
-                +
-                `<video id="${userId}" autoplay></video>
+                <img id="microphone-${userId}" title="mute" src="resources/logos/microphone-close.svg">
+                <button id="report-${userId}" class="report">
+                    <img title="report this user" src="resources/logos/report.svg">
+                    <span>Report/Block</span>
+                </button>
+                <video id="${userId}" autoplay></video>
+                <img src="resources/logos/blockSign.svg" id="blocking-${userId}" class="block-logo">
             </div>
         `;
 
         layoutManager.add(DivImportance.Normal, userId, html);
-
+        
         this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail<HTMLVideoElement>(userId));
 
         //permit to create participant in discussion part
@@ -510,18 +505,17 @@ export class MediaManager {
         };
         this.addNewParticipant(userId, userName, undefined, showReportUser);
 
-        if(!anonymous){
-            const reportBanUserAction: HTMLImageElement = HtmlUtils.getElementByIdOrFail<HTMLImageElement>(`report-${userId}`);
-            reportBanUserAction.addEventListener('click', (e) => {
-                e.preventDefault();
-                showReportUser();
-            });
-        }
+        const reportBanUserActionEl: HTMLImageElement = HtmlUtils.getElementByIdOrFail<HTMLImageElement>(`report-${userId}`);
+        reportBanUserActionEl.addEventListener('click', (e) => {
+            e.preventDefault();
+            e.stopPropagation();
+            showReportUser();
+        });
     }
     
     addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){
 
-        userId = `screen-sharing-${userId}`;
+        userId = this.getScreenSharingId(userId);
         const html = `
             <div id="div-${userId}" class="video-container">
                 <video id="${userId}" autoplay></video>
@@ -532,7 +526,11 @@ export class MediaManager {
 
         this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail<HTMLVideoElement>(userId));
     }
-    
+
+    private getScreenSharingId(userId: string): string {
+        return `screen-sharing-${userId}`;
+    }
+
     disabledMicrophoneByUserId(userId: number){
         const element = document.getElementById(`microphone-${userId}`);
         if(!element){
@@ -571,6 +569,10 @@ export class MediaManager {
         }
     }
 
+    toggleBlockLogo(userId: number, show: boolean): void {
+        const blockLogoElement = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('blocking-'+userId);
+        show ? blockLogoElement.classList.add('active') : blockLogoElement.classList.remove('active');
+    }
     addStreamRemoteVideo(userId: string, stream : MediaStream): void {
         const remoteVideo = this.remoteVideo.get(userId);
         if (remoteVideo === undefined) {
@@ -580,12 +582,12 @@ export class MediaManager {
     }
     addStreamRemoteScreenSharing(userId: string, stream : MediaStream){
         // In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
-        const remoteVideo = this.remoteVideo.get(`screen-sharing-${userId}`);
+        const remoteVideo = this.remoteVideo.get(this.getScreenSharingId(userId));
         if (remoteVideo === undefined) {
             this.addScreenSharingActiveVideo(userId);
         }
 
-        this.addStreamRemoteVideo(`screen-sharing-${userId}`, stream);
+        this.addStreamRemoteVideo(this.getScreenSharingId(userId), stream);
     }
     
     removeActiveVideo(userId: string){
@@ -596,7 +598,7 @@ export class MediaManager {
         this.removeParticipant(userId);
     }
     removeActiveScreenSharingVideo(userId: string) {
-        this.removeActiveVideo(`screen-sharing-${userId}`)
+        this.removeActiveVideo(this.getScreenSharingId(userId))
     }
     
     playWebrtcOutSound(): void {
@@ -632,7 +634,7 @@ export class MediaManager {
         errorDiv.style.display = 'block';
     }
     isErrorScreenSharing(userId: string): void {
-        this.isError(`screen-sharing-${userId}`);
+        this.isError(this.getScreenSharingId(userId));
     }
 
 
diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts
index bc2590d753128a35bf074a823d5ada5a187eca71..98f83b0c657677cb8ac73d727c33a3f34185aa73 100644
--- a/front/src/WebRtc/SimplePeer.ts
+++ b/front/src/WebRtc/SimplePeer.ts
@@ -9,10 +9,11 @@ import {
     UpdatedLocalStreamCallback
 } from "./MediaManager";
 import {ScreenSharingPeer} from "./ScreenSharingPeer";
-import {MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
+import {MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
 import {RoomConnection} from "../Connexion/RoomConnection";
 import {connectionManager} from "../Connexion/ConnectionManager";
 import {GameConnexionTypes} from "../Url/UrlManager";
+import {blackListManager} from "./BlackListManager";
 
 export interface UserSimplePeerInterface{
     userId: number;
@@ -38,6 +39,7 @@ export class SimplePeer {
     private readonly sendLocalScreenSharingStreamCallback: StartScreenSharingCallback;
     private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback;
     private readonly peerConnectionListeners: Array<PeerConnectionListener> = new Array<PeerConnectionListener>();
+    private readonly userId: number;
 
     constructor(private Connection: RoomConnection, private enableReporting: boolean, private myName: string) {
         // We need to go through this weird bound function pointer in order to be able to "free" this reference later.
@@ -48,6 +50,7 @@ export class SimplePeer {
         mediaManager.onUpdateLocalStream(this.sendLocalVideoStreamCallback);
         mediaManager.onStartScreenSharing(this.sendLocalScreenSharingStreamCallback);
         mediaManager.onStopScreenSharing(this.stopLocalScreenSharingStreamCallback);
+        this.userId = Connection.getUserId();
         this.initialise();
     }
 
@@ -91,8 +94,7 @@ export class SimplePeer {
         });
     }
 
-    private receiveWebrtcStart(user: UserSimplePeerInterface) {
-        //this.WebRtcRoomId = data.roomId;
+    private receiveWebrtcStart(user: UserSimplePeerInterface): void {
         this.Users.push(user);
         // Note: the clients array contain the list of all clients (even the ones we are already connected to in case a user joints a group)
         // So we can receive a request we already had before. (which will abort at the first line of createPeerConnection)
@@ -136,13 +138,13 @@ export class SimplePeer {
 
         mediaManager.removeActiveVideo("" + user.userId);
 
-        mediaManager.addActiveVideo("" + user.userId, name, connectionManager.getConnexionType === GameConnexionTypes.anonymous);
+        mediaManager.addActiveVideo(user, name);
 
-        const peer = new VideoPeer(user.userId, user.initiator ? user.initiator : false, this.Connection);
+        const peer = new VideoPeer(user, user.initiator ? user.initiator : false, this.Connection);
 
         //permit to send message
         mediaManager.addSendMessageCallback(user.userId,(message: string) => {
-            peer.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_MESSAGE, name: this.myName.toUpperCase(), message: message})));
+            peer.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_MESSAGE, name: this.myName.toUpperCase(), userId: this.userId, message: message})));
         });
 
         peer.toClose = false;
@@ -298,6 +300,7 @@ export class SimplePeer {
     }
 
     private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) {
+        if (blackListManager.isBlackListed(data.userId)) return;
         console.log("receiveWebrtcScreenSharingSignal", data);
         try {
             //if offer type, create peer connection
@@ -390,6 +393,7 @@ export class SimplePeer {
     }
 
     private sendLocalScreenSharingStreamToUser(userId: number): void {
+        if (blackListManager.isBlackListed(userId)) return;
         // If a connection already exists with user (because it is already sharing a screen with us... let's use this connection)
         if (this.PeerScreenSharingConnectionArray.has(userId)) {
             this.pushScreenSharingToRemoteUser(userId);
diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts
index cc5ac62ef433ecc16fc5bde52deea99ab21eb03a..b2df80c2081911707791392d9933742bc6affa7d 100644
--- a/front/src/WebRtc/VideoPeer.ts
+++ b/front/src/WebRtc/VideoPeer.ts
@@ -2,19 +2,30 @@ import * as SimplePeerNamespace from "simple-peer";
 import {mediaManager} from "./MediaManager";
 import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable";
 import {RoomConnection} from "../Connexion/RoomConnection";
+import {blackListManager} from "./BlackListManager";
+import {Subscription} from "rxjs";
+import {UserSimplePeerInterface} from "./SimplePeer";
 
 const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
 
 export const MESSAGE_TYPE_CONSTRAINT = 'constraint';
 export const MESSAGE_TYPE_MESSAGE = 'message';
+export const MESSAGE_TYPE_BLOCKED = 'blocked';
+export const MESSAGE_TYPE_UNBLOCKED = 'unblocked';
 /**
  * A peer connection used to transmit video / audio signals between 2 peers.
  */
 export class VideoPeer extends Peer {
     public toClose: boolean = false;
     public _connected: boolean = false;
-
-    constructor(public userId: number, initiator: boolean, private connection: RoomConnection) {
+    private remoteStream!: MediaStream;
+    private blocked: boolean = false;
+    private userId: number;
+    private userName: string;
+    private onBlockSubscribe: Subscription;
+    private onUnBlockSubscribe: Subscription;
+
+    constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
         super({
             initiator: initiator ? initiator : false,
             reconnectTimer: 10000,
@@ -31,35 +42,15 @@ export class VideoPeer extends Peer {
                 ]
             }
         });
-
-        console.log('PEER SETUP ', {
-            initiator: initiator ? initiator : false,
-            reconnectTimer: 10000,
-            config: {
-                iceServers: [
-                    {
-                        urls: STUN_SERVER.split(',')
-                    },
-                    {
-                        urls: TURN_SERVER.split(','),
-                        username: TURN_USER,
-                        credential: TURN_PASSWORD
-                    },
-                ]
-            }
-        });
+        this.userId = user.userId;
+        this.userName = user.name || '';
 
         //start listen signal for the peer connection
         this.on('signal', (data: unknown) => {
             this.sendWebrtcSignal(data);
         });
 
-        this.on('stream', (stream: MediaStream) => {
-            this.stream(stream);
-        });
-
-        /*peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => {
-        });*/
+        this.on('stream', (stream: MediaStream) => this.stream(stream));
 
         this.on('close', () => {
             this._connected = false;
@@ -70,7 +61,7 @@ export class VideoPeer extends Peer {
         // eslint-disable-next-line @typescript-eslint/no-explicit-any
         this.on('error', (err: any) => {
             console.error(`error => ${this.userId} => ${err.code}`, err);
-            mediaManager.isError("" + userId);
+            mediaManager.isError("" + this.userId);
         });
 
         this.on('connect', () => {
@@ -81,8 +72,6 @@ export class VideoPeer extends Peer {
 
         this.on('data',  (chunk: Buffer) => {
             const message = JSON.parse(chunk.toString('utf8'));
-            console.log("data", message);
-
             if(message.type === MESSAGE_TYPE_CONSTRAINT) {
                 if (message.audio) {
                     mediaManager.enabledMicrophoneByUserId(this.userId);
@@ -95,8 +84,19 @@ export class VideoPeer extends Peer {
                 } else {
                     mediaManager.disabledVideoByUserId(this.userId);
                 }
-            } else if(message.type === 'message') {
-                mediaManager.addNewMessage(message.name, message.message);
+            } else if(message.type === MESSAGE_TYPE_MESSAGE) {
+                if (!blackListManager.isBlackListed(message.userId)) {
+                    mediaManager.addNewMessage(message.name, message.message);
+                }
+            } else if(message.type === MESSAGE_TYPE_BLOCKED) {
+                //FIXME when A blacklists B, the output stream from A is muted in B's js client. This is insecure since B can manipulate the code to unmute A stream. 
+                // Find a way to block A's output stream in A's js client
+                //However, the output stream stream B is correctly blocked in A client
+                this.blocked = true;
+                this.toggleRemoteStream(false);
+            } else if(message.type === MESSAGE_TYPE_UNBLOCKED) {
+                this.blocked = false;
+                this.toggleRemoteStream(true);
             }
         });
 
@@ -105,6 +105,31 @@ export class VideoPeer extends Peer {
         });
 
         this.pushVideoToRemoteUser();
+        this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userId) => {
+            if (userId === this.userId) {
+                this.toggleRemoteStream(false);
+                this.sendBlockMessage(true);
+            }
+        });
+        this.onUnBlockSubscribe = blackListManager.onUnBlockStream.subscribe((userId) => {
+            if (userId === this.userId) {
+                this.toggleRemoteStream(true);
+                this.sendBlockMessage(false);
+            }
+        });
+        
+        if (blackListManager.isBlackListed(this.userId)) {
+            this.sendBlockMessage(true)
+        }
+    }
+
+    private sendBlockMessage(blocking: boolean) {
+        this.write(new Buffer(JSON.stringify({type: blocking ? MESSAGE_TYPE_BLOCKED : MESSAGE_TYPE_UNBLOCKED, name: this.userName.toUpperCase(), userId: this.userId, message: ''})));
+    }
+
+    private toggleRemoteStream(enable: boolean) {
+        this.remoteStream.getTracks().forEach(track => track.enabled = enable);
+        mediaManager.toggleBlockLogo(this.userId, !enable);
     }
 
     private sendWebrtcSignal(data: unknown) {
@@ -120,13 +145,13 @@ export class VideoPeer extends Peer {
      */
     private stream(stream: MediaStream) {
         try {
+            this.remoteStream = stream;
+            if (blackListManager.isBlackListed(this.userId) || this.blocked) {
+                this.toggleRemoteStream(false);
+            }
             mediaManager.addStreamRemoteVideo("" + this.userId, stream);
         }catch (err){
             console.error(err);
-            //Force add streem video
-            /*setTimeout(() => {
-                this.stream(stream);
-            }, 500);*/ //todo: find a way to prevent infinite regression.
         }
     }
 
@@ -139,6 +164,8 @@ export class VideoPeer extends Peer {
             if(!this.toClose){
                 return;
             }
+            this.onBlockSubscribe.unsubscribe();
+            this.onUnBlockSubscribe.unsubscribe();
             mediaManager.removeActiveVideo("" + this.userId);
             // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray"
             // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel.
diff --git a/front/yarn.lock b/front/yarn.lock
index fd34cd6d82ea497834b8a6dfdbcba5bccbfd6db5..0b85ad88995e0ed2db3ebce4c3aa6dc9510b384d 100644
--- a/front/yarn.lock
+++ b/front/yarn.lock
@@ -4094,7 +4094,7 @@ run-queue@^1.0.0, run-queue@^1.0.3:
   dependencies:
     aproba "^1.1.1"
 
-rxjs@^6.6.0:
+rxjs@^6.6.0, rxjs@^6.6.3:
   version "6.6.3"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
   integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==