diff --git a/frontend/src/pages/Play/ChessGameMultiplayer.jsx b/frontend/src/pages/Play/ChessGameMultiplayer.jsx
new file mode 100644
index 0000000..58f7aac
--- /dev/null
+++ b/frontend/src/pages/Play/ChessGameMultiplayer.jsx
@@ -0,0 +1,175 @@
+import React, { useContext, useEffect, useState } from 'react'
+
+import { useNavigate } from 'react-router-dom'
+import { useDisclosure } from '@mantine/hooks'
+import { Avatar, Button, Flex, Image, MediaQuery, Modal, NavLink, Text, Title } from '@mantine/core'
+
+import { socket, socketBot } from '../../socket'
+import { getUserData } from '../../utils/auth'
+import { ChessGameContext } from '../../context/chess-game-context'
+import ChessBoard from '../Chess/ChessBoard'
+import GameHistory from '../../components/GameHistory'
+import MainLoader from '../../components/MainLoader'
+import { SOCKET_EVENTS } from '../../constants'
+import Timer from '../Chess/Timer'
+const { CONNECT, DISCONNECT, CHESS_OPPONENT_MOVE, USER_RESIGNED, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM, CHESS_MOVE } = SOCKET_EVENTS;
+
+const ChessGameMultiplayer = () => {
+ const { setGameHistory, hasGameEnded, gameEndedReason, endGame, handleOpponentMove, isTimerOn } = useContext(ChessGameContext);
+ const [gameEndedModalOpen, modalFunctions] = useDisclosure(true);
+
+ const user = getUserData();
+ let username = user.username;
+ let userid = user.id;
+ let color = localStorage.getItem('myColor');
+ const [hasJoinedRoom, setHasJoinedRoom] = useState(localStorage.getItem('socketid'));
+ const [isWaiting, setIsWaiting] = useState(true);
+ const roomID = localStorage.getItem('roomID');
+ const navigate = useNavigate();
+ const opponent = localStorage.getItem('opponent');
+
+ const exitGame = () => {
+ // cleanup game related data
+ localStorage.removeItem('socketid');
+ localStorage.removeItem('roomID');
+ localStorage.removeItem('opponent');
+ localStorage.removeItem('myColor');
+ localStorage.removeItem('timeLimit');
+ socket.disconnect();
+ navigate('/play/friend');
+ }
+
+ const resign = () => {
+ socket.emit(USER_RESIGNED, roomID, username);
+ endGame('RESIGN');
+ exitGame();
+ }
+ const pieceDropCallback = (moveData) => {
+ socket.emit(CHESS_MOVE, roomID, moveData);
+ }
+
+ const pieceClickCallback = (moveData) => {
+ // moveData contains fen string, from, to squares of the move
+ socket.emit(CHESS_MOVE, roomID, moveData);
+ }
+
+ useEffect(() => {
+ socket.connect();
+ socket.on(CONNECT, () => {
+ console.log('Connected');
+ });
+
+ socket.on(JOIN_ROOM_SUCCESS, (fetchedGameHistory) => {
+ setGameHistory(fetchedGameHistory);
+ setHasJoinedRoom(true);
+ });
+
+ socket.on(ROOM_FULL, () => {
+ console.log('Room is full');
+ })
+
+ socket.on(JOIN_ROOM_ERROR, (err) => {
+ console.error("Error:", err);
+ })
+
+ socket.emit(JOIN_ROOM, roomID, { username, color, userid })
+
+ socket.on(DISCONNECT, (reason) => {
+ console.log('Socket disconnected due to', reason);
+ });
+
+ socket.on(CHESS_OPPONENT_MOVE, (data) => {
+ handleOpponentMove(data, () => {
+ socket.emit(GAME_END, roomID);
+ })
+ })
+
+ socket.on(USER_JOINED_ROOM, () => {
+ setIsWaiting(false);
+ });
+
+ socket.on(USER_RESIGNED, () => {
+ endGame('RESIGN');
+ });
+
+ return () => {
+ socket.offAny();
+ socket.disconnect();
+ }
+ }, []);
+
+ if (!hasJoinedRoom) return (
+