diff --git a/web/next.config.ts b/web/next.config.ts
index 4d4174c..d68a004 100644
--- a/web/next.config.ts
+++ b/web/next.config.ts
@@ -6,11 +6,11 @@ const nextConfig: NextConfig = {
     images: {
 		remotePatterns: [
 			{
-				protocol: 'https',
-				hostname: 'api.ic3.space',
-				pathname: '/strafe/map-images/**',
-				port: '',
-				search: '',
+				protocol: "https",
+				hostname: "tr.rbxcdn.com",
+				pathname: "/**",
+				port: "",
+				search: "",
 			},
 		],
 	},
diff --git a/web/src/app/mapfixes/_card.tsx b/web/src/app/_components/mapCard.tsx
similarity index 79%
rename from web/src/app/mapfixes/_card.tsx
rename to web/src/app/_components/mapCard.tsx
index a1c594a..3e4269a 100644
--- a/web/src/app/mapfixes/_card.tsx
+++ b/web/src/app/_components/mapCard.tsx
@@ -6,8 +6,9 @@ import { Rating } from "@mui/material";
 interface SubmissionCardProps {
 	displayName: string;
 	assetId: number;
-	rating: number;
+	authorId: number;
 	author: string;
+	rating: number;
 	id: number;
 }
 
@@ -18,7 +19,7 @@ export default function SubmissionCard(props: SubmissionCardProps) {
 				<div className="content">
 					<div className="map-image">
 						{/* TODO: Grab image of model */}
-						<Image height={200} width={200} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" style={{ width: `100%` }} alt={props.displayName} />
+						<Image width={230} height={230} layout="fixed" priority={true} src={`/thumbnails/asset/${props.assetId}`} alt={props.displayName} />
 					</div>
 					<div className="details">
 						<div className="header">
@@ -29,7 +30,7 @@ export default function SubmissionCard(props: SubmissionCardProps) {
 						</div>
 						<div className="footer">
 							<div className="author">
-								<Image className="avatar" width={28} height={28} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" alt={props.author}/>
+								<Image className="avatar" width={28} height={28} priority={true} src={`/thumbnails/user/${props.authorId}`} alt={props.author}/>
 								<span>{props.author}</span>
 							</div>
 						</div>
diff --git a/web/src/app/mapfixes/(styles)/page/card.scss b/web/src/app/_components/styles/mapCard.scss
similarity index 93%
rename from web/src/app/mapfixes/(styles)/page/card.scss
rename to web/src/app/_components/styles/mapCard.scss
index dce43eb..cfcb9d2 100644
--- a/web/src/app/mapfixes/(styles)/page/card.scss
+++ b/web/src/app/_components/styles/mapCard.scss
@@ -1,4 +1,4 @@
-@use "../../../globals.scss";
+@use "../../globals.scss";
 
 .submissionCard {
 	display: flex;
@@ -9,7 +9,7 @@
 	flex-direction: column;
 	justify-content: space-between;
 	height: 100%;
-	min-width: 180px;
+	min-width: 230px;
 	max-width: 340px;
 }
 
@@ -43,8 +43,6 @@
 	overflow: hidden;
 
 	> img {
-		width: 100%;
-		height: 100%;
 		object-fit: cover;
 	}
 }
diff --git a/web/src/app/mapfixes/(styles)/page.scss b/web/src/app/mapfixes/(styles)/page.scss
index bf98951..daf65f9 100644
--- a/web/src/app/mapfixes/(styles)/page.scss
+++ b/web/src/app/mapfixes/(styles)/page.scss
@@ -1,4 +1,4 @@
-@forward "./page/card.scss";
+@forward "../../_components/styles/mapCard.scss";
 
 @use "../../globals.scss";
 
diff --git a/web/src/app/mapfixes/page.tsx b/web/src/app/mapfixes/page.tsx
index cf6eccb..743062d 100644
--- a/web/src/app/mapfixes/page.tsx
+++ b/web/src/app/mapfixes/page.tsx
@@ -2,9 +2,11 @@
 
 import React, { useState, useEffect } from "react";
 import { MapfixInfo } from "../ts/Mapfix";
-import MapfixCard from "./_card";
+import MapfixCard from "../_components/mapCard";
 import Webpage from "@/app/_components/webpage";
 
+// TODO: MAKE MAPFIX & SUBMISSIONS USE THE SAME COMPONENTS :angry: (currently too lazy)
+
 import "./(styles)/page.scss";
 
 export default function MapfixInfoPage() {
@@ -100,6 +102,7 @@ export default function MapfixInfoPage() {
 							assetId={mapfix.AssetID}
 							displayName={mapfix.DisplayName}
 							author={mapfix.Creator}
+							authorId={mapfix.Submitter}
 							rating={mapfix.StatusID}
 						/>
 					))}
diff --git a/web/src/app/submissions/(styles)/page.scss b/web/src/app/submissions/(styles)/page.scss
index bf98951..d54fd38 100644
--- a/web/src/app/submissions/(styles)/page.scss
+++ b/web/src/app/submissions/(styles)/page.scss
@@ -1,4 +1,4 @@
-@forward "./page/card.scss";
+@forward "../../_components/styles/mapCard.scss";
 
 @use "../../globals.scss";
 
@@ -27,7 +27,7 @@ a {
 
 @media (max-width: 768px) {
 	.grid {
-		grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+		grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
 	}
 }
 
diff --git a/web/src/app/submissions/(styles)/page/card.scss b/web/src/app/submissions/(styles)/page/card.scss
deleted file mode 100644
index dce43eb..0000000
--- a/web/src/app/submissions/(styles)/page/card.scss
+++ /dev/null
@@ -1,87 +0,0 @@
-@use "../../../globals.scss";
-
-.submissionCard {
-	display: flex;
-	background-color: #2020207c;
-	border: 1px solid #97979783;
-	border-radius: 10px;
-	box-sizing: border-box;
-	flex-direction: column;
-	justify-content: space-between;
-	height: 100%;
-	min-width: 180px;
-	max-width: 340px;
-}
-
-.content {
-	display: flex;
-	flex-grow: 1;
-	flex-direction: column;
-	justify-content: space-between;
-}
-
-.details {
-	padding: 2px 4px;
-}
-
-.header {
-	display: flex;
-	justify-content: space-between;
-	align-items: center;
-	margin: 3px 0px;
-}
-
-.footer {
-	display: flex;
-	justify-content: space-between;
-	align-items: center;
-	margin: 3px 0px;
-}
-
-.map-image {
-	border-radius: 10px 10px 0 0;
-	overflow: hidden;
-
-	> img {
-		width: 100%;
-		height: 100%;
-		object-fit: cover;
-	}
-}
-
-.displayName {
-	font-size: 1rem;
-	font-weight: bold;
-	overflow: hidden;
-	max-width: 70%;
-	text-overflow: ellipsis;
-	white-space: nowrap;
-}
-
-.rating {
-	flex-shrink: 0;
-}
-
-.author {
-	display: flex;
-	align-items: center;
-	gap: 0.5rem;
-	flex-grow: 1;
-	min-width: 0;
-}
-
-.author span {
-	white-space: nowrap;
-	overflow: hidden;
-	text-overflow: ellipsis;
-}
-
-.rating {
-	margin-left: auto;
-	flex-shrink: 0;
-}
-
-.avatar {
-	border-radius: 50%;
-	object-fit: cover;
-}
\ No newline at end of file
diff --git a/web/src/app/submissions/[submissionId]/(styles)/page/map.scss b/web/src/app/submissions/[submissionId]/(styles)/page/map.scss
index ede388e..12469f0 100644
--- a/web/src/app/submissions/[submissionId]/(styles)/page/map.scss
+++ b/web/src/app/submissions/[submissionId]/(styles)/page/map.scss
@@ -5,8 +5,12 @@
 	display: flex;
 	justify-content: center;
 	align-items: center;
-	width: 350px;
-	height: 350px;
+	width: 100%;
+	height: auto;
+	margin-left: auto;
+	margin-right: auto;
+	border-radius: 12px;
+	overflow: hidden;
 
 	> p {
 		text-align: center;
diff --git a/web/src/app/submissions/[submissionId]/(styles)/page/review.scss b/web/src/app/submissions/[submissionId]/(styles)/page/review.scss
index 08fc5c0..2ca4539 100644
--- a/web/src/app/submissions/[submissionId]/(styles)/page/review.scss
+++ b/web/src/app/submissions/[submissionId]/(styles)/page/review.scss
@@ -40,8 +40,7 @@
     gap: 25px;
 
     img {
-        width: 100%;
-        height: 350px;
+        height: 100%;
         object-fit: contain
     }
 }
\ No newline at end of file
diff --git a/web/src/app/submissions/[submissionId]/_map.tsx b/web/src/app/submissions/[submissionId]/_map.tsx
deleted file mode 100644
index e364f70..0000000
--- a/web/src/app/submissions/[submissionId]/_map.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { SubmissionInfo } from "@/app/ts/Submission"
-
-interface AssetID {
-    id: SubmissionInfo["AssetID"]
-}
-
-function MapImage() {
-    return <p>Fetching map image...</p>
-}
-
-export {
-	type AssetID,
-	MapImage
-}
\ No newline at end of file
diff --git a/web/src/app/submissions/[submissionId]/_mapImage.tsx b/web/src/app/submissions/[submissionId]/_mapImage.tsx
new file mode 100644
index 0000000..96f9059
--- /dev/null
+++ b/web/src/app/submissions/[submissionId]/_mapImage.tsx
@@ -0,0 +1,28 @@
+import Image from "next/image";
+import { SubmissionInfo } from "@/app/ts/Submission";
+
+interface AssetID {
+	id: SubmissionInfo["AssetID"];
+}
+
+function MapImage({ id }: AssetID) {
+	if (!id) {
+		return <p>Missing asset ID</p>;
+	}
+
+	const imageUrl = `/thumbnails/asset/${id}`;
+
+	return (
+		<Image
+			src={imageUrl}
+			alt="Map Thumbnail"
+			layout="responsive"
+			width={512}
+			height={512}
+			priority={true}
+			className="map-image"
+		/>
+	);
+}
+
+export { type AssetID, MapImage };
diff --git a/web/src/app/submissions/[submissionId]/page.tsx b/web/src/app/submissions/[submissionId]/page.tsx
index 9dae81f..c78ba8b 100644
--- a/web/src/app/submissions/[submissionId]/page.tsx
+++ b/web/src/app/submissions/[submissionId]/page.tsx
@@ -2,7 +2,7 @@
 
 import { SubmissionInfo, SubmissionStatusToString } from "@/app/ts/Submission";
 import type { CreatorAndReviewStatus } from "./_comments";
-import { MapImage } from "./_map";
+import { MapImage } from "./_mapImage";
 import { useParams } from "next/navigation";
 import ReviewButtons from "./_reviewButtons";
 import { Rating } from "@mui/material";
@@ -15,7 +15,8 @@ import { useState, useEffect } from "react";
 import "./(styles)/page.scss";
 
 interface ReviewId {
-	submissionId: string
+	submissionId: string;
+	assetId: number;
 }
 
 function Ratings() {
@@ -43,7 +44,7 @@ function RatingArea(submission: ReviewId) {
     return (
         <aside className="review-area">
         	<section className="map-image-area">
-         		<MapImage/>
+         		<MapImage id={submission.assetId}/>
          	</section>
             <Ratings/>
 			<ReviewButtons submissionId={submission.submissionId}/>
@@ -96,7 +97,7 @@ export default function SubmissionInfoPage() {
         <Webpage>
             <main className="map-page-main">
                 <section className="review-section">
-					<RatingArea submissionId={dynamicId.submissionId}/>
+					<RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId}/>
                     <TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} comments={[]}/>
                 </section>
             </main>
diff --git a/web/src/app/submissions/_card.tsx b/web/src/app/submissions/_card.tsx
deleted file mode 100644
index 29a6793..0000000
--- a/web/src/app/submissions/_card.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from "react";
-import Image from "next/image";
-import Link from "next/link";
-import { Rating } from "@mui/material";
-
-interface SubmissionCardProps {
-	displayName: string;
-	assetId: number;
-	rating: number;
-	author: string;
-	id: number;
-}
-
-export default function SubmissionCard(props: SubmissionCardProps) {
-	return (
-		<Link href={`/submissions/${props.id}`}>
-			<div className="submissionCard">
-				<div className="content">
-					<div className="map-image">
-						{/* TODO: Grab image of model */}
-						<Image height={200} width={200} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" style={{ width: `100%` }} alt={props.displayName} />
-					</div>
-					<div className="details">
-						<div className="header">
-							<span className="displayName">{props.displayName}</span>
-							<div className="rating">
-								<Rating value={props.rating} readOnly size="small" />
-							</div>
-						</div>
-						<div className="footer">
-							<div className="author">
-								<Image className="avatar" width={28} height={28} priority={true} src="https://api.ic3.space/strafe/map-images/11222350808" alt={props.author}/>
-								<span>{props.author}</span>
-							</div>
-						</div>
-					</div>
-				</div>
-			</div>
-		</Link>
-	);
-}
\ No newline at end of file
diff --git a/web/src/app/submissions/page.tsx b/web/src/app/submissions/page.tsx
index 2b803a7..05d3ce7 100644
--- a/web/src/app/submissions/page.tsx
+++ b/web/src/app/submissions/page.tsx
@@ -2,7 +2,7 @@
 
 import React, { useState, useEffect } from "react";
 import { SubmissionInfo } from "../ts/Submission";
-import SubmissionCard from "./_card";
+import SubmissionCard from "../_components/mapCard";
 import Webpage from "@/app/_components/webpage";
 
 import "./(styles)/page.scss";
@@ -100,6 +100,7 @@ export default function SubmissionInfoPage() {
 							assetId={submission.AssetID}
 							displayName={submission.DisplayName}
 							author={submission.Creator}
+							authorId={submission.Submitter}
 							rating={submission.StatusID}
 						/>
 					))}
diff --git a/web/src/app/thumbnails/asset/[assetId]/route.ts b/web/src/app/thumbnails/asset/[assetId]/route.ts
new file mode 100644
index 0000000..b7992f4
--- /dev/null
+++ b/web/src/app/thumbnails/asset/[assetId]/route.ts
@@ -0,0 +1,69 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+export async function GET(
+	request: NextRequest,
+	context: { params: Promise<{ assetId: number }> }
+): Promise<NextResponse> {
+	const { assetId } = await context.params;
+	
+	if (!assetId) {
+		return NextResponse.json(
+			{ error: 'Missing asset ID' },
+			{ status: 400 }
+		);
+	}
+
+	let finalAssetId = assetId;
+
+	try {
+		const mediaResponse = await fetch(
+			`https://publish.roblox.com/v1/assets/${assetId}/media`
+		);
+		if (mediaResponse.ok) {
+			const mediaData = await mediaResponse.json();
+			if (mediaData.data && mediaData.data.length > 0) {
+				finalAssetId = mediaData.data[0].toString();
+			}
+		}
+	} catch { }
+
+	try {
+		const response = await fetch(
+			`https://thumbnails.roblox.com/v1/assets?format=png&size=512x512&assetIds=${finalAssetId}`
+		);
+
+		if (!response.ok) {
+			throw new Error('Failed to fetch thumbnail JSON');
+		}
+
+		const data = await response.json();
+
+		const imageUrl = data.data[0]?.imageUrl;
+		if (!imageUrl) {
+			return NextResponse.json(
+				{ error: 'No image URL found in the response' },
+				{ status: 404 }
+			);
+		}
+
+		const imageResponse = await fetch(imageUrl);
+		if (!imageResponse.ok) {
+			throw new Error('Failed to fetch the image');
+		}
+
+		const arrayBuffer = await imageResponse.arrayBuffer();
+		const buffer = Buffer.from(arrayBuffer);
+
+		return new NextResponse(buffer, {
+			headers: {
+				'Content-Type': 'image/png',
+				'Content-Length': buffer.length.toString(),
+			},
+		});
+	} catch {
+		return NextResponse.json(
+			{ error: 'Failed to fetch or process thumbnail' },
+			{ status: 500 }
+		);
+	}
+}
\ No newline at end of file
diff --git a/web/src/app/thumbnails/user/[userId]/route.ts b/web/src/app/thumbnails/user/[userId]/route.ts
new file mode 100644
index 0000000..47bef3c
--- /dev/null
+++ b/web/src/app/thumbnails/user/[userId]/route.ts
@@ -0,0 +1,55 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+export async function GET(
+	request: NextRequest,
+	context: { params: Promise<{ userId: number }> }
+): Promise<NextResponse> {
+	const { userId } = await context.params; // Await params to access userId
+
+	if (!userId) {
+		return NextResponse.json(
+			{ error: 'Missing userId parameter' },
+			{ status: 400 }
+		);
+	}
+
+	try {
+		const response = await fetch(
+			`https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds=${userId}&size=420x420&format=Png&isCircular=false`
+		);
+
+		if (!response.ok) {
+			throw new Error('Failed to fetch avatar headshot JSON');
+		}
+
+		const data = await response.json();
+
+		const imageUrl = data.data[0]?.imageUrl;
+		if (!imageUrl) {
+			return NextResponse.json(
+				{ error: 'No image URL found in the response' },
+				{ status: 404 }
+			);
+		}
+
+		const imageResponse = await fetch(imageUrl);
+		if (!imageResponse.ok) {
+			throw new Error('Failed to fetch the image');
+		}
+
+		const arrayBuffer = await imageResponse.arrayBuffer();
+		const buffer = Buffer.from(arrayBuffer);
+
+		return new NextResponse(buffer, {
+			headers: {
+				'Content-Type': 'image/png',
+				'Content-Length': buffer.length.toString(),
+			},
+		});
+	} catch {
+		return NextResponse.json(
+			{ error: 'Failed to fetch or process avatar headshot' },
+			{ status: 500 }
+		);
+	}
+}