Achieve Game-Like Scaling in Discord Activities and Web Apps Using CSS
Tags: javascript, programming, node, discord
Published: 2024-06-03 15:30:00

**[Discord Activities](https://support.discord.com/hc/en-us/articles/4422142836759-Activities-on-Discord)** are awesome! They run inside Discord and can be all sorts of cool things like games, quizzes, and more. But if you're developing using web technologies rather than game engines, you might run into a common problem: scaling.

In this article, we'll show you how to achieve game-like scaling in your Discord Activities and web apps using plain CSS. This will allow you to create responsive designs that look great on any device, **[just like a game](https://docs.unity3d.com/2020.1/Documentation/Manual/HOWTO-UIMultiResolution.html)**!

![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ak6x8twhs5fbnc7q15x.png)

## The Problem with Scaling

Screen sizes vary widely, even among devices of the same type. This can make it challenging to create a design that looks good on all screens. If you've ever built a Discord Activity using HTML and CSS, you've probably encountered this issue. 

Suppose you have a button with a fixed size. On a large desktop screen, the button might look tiny, while on a smaller screen, it might be too big and take up a large portion of the screen. That's fine for standard web apps, but for Discord Activities, you may want everyone to have a similar experience, regardless of their screen size.

This is even more of a problem when you minimize the activity window or navigate elsewhere in Discord. Your page might look tiny and miss out a lot of the content, or it might be too large and require scrolling to see everything!

Before scaling, content looks huge and most of it is cut off:

![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dui0h7pkqqs7htb6r9ql.png)

After scaling, content fits the screen and is fully visible:

![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qe2qhertaml03di76vdr.png)

## Create a Discord Activity Project

**[Create a Discord Activity project](https://dev.to/waveplay/how-to-build-a-discord-activity-easily-with-robojs-5bng)** if you don't have one already:

```bash
npx create-robo my-activity -k activity
```

We'll be using **[React](https://react.dev)** and **[TypeScript](https://docs.roboplay.dev/robojs/typescript)** for this example, but you can apply the same principles to any frontend framework or vanilla **[HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)** and **[CSS](https://developer.mozilla.org/en-US/docs/Web/CSS)**.

We highly recommend using **[create-robo](https://docs.roboplay.dev/cli/create-robo)** to create a **Discord Activity** project. **[Robo.js](https://roboplay.dev/docs)** providers a lot of features and tools to help you build your activity faster and more efficiently, such as **[Multiplayer Support](https://dev.to/waveplay/how-to-add-multiplayer-to-your-discord-activity-lo1)**, **[Easy Hosting](https://docs.roboplay.dev/discord-activities/hosting)**, **[Streamlined Tunnels](https://docs.roboplay.dev/discord-activities/tunnels)**, **[Built-in Database](https://docs.roboplay.dev/robojs/flashcore)**, **[Plugin System](https://docs.roboplay.dev/plugins/overview)**, and so much more!

{% embed https://dev.to/waveplay/how-to-build-a-discord-activity-easily-with-robojs-5bng %}

## Achieving Game-Like Scaling

First, decide on a base resolution for your activity. This will be the resolution you design your activity for. For this example, let's use `1280x720`. This is a common resolution for games and will work well for our purposes, especially since Discord enforces this aspect ratio for activities.

In game design, developers often create assets for a **[base resolution](https://gamemaker.io/en/tutorials/the-basics-of-scaling-the-game-camera)** and scale up or down from there to fit the player's screen. This ensures consistency in the visual experience. We can apply the same principle to our Discord Activities and web apps using CSS via the `transform` property.

Next, let's create a wrapper component that will handle the scaling for us. We'll use the `transform` property to scale the content based on the user's screen size. Here's a simple example:

```tsx
// src/components/ScaleProvider.tsx
import { createContext, useContext, useState, useEffect, ReactNode } from 'react'

interface ScaleContextType {
	scale: number
}

const ScaleContext = createContext<ScaleContextType | undefined>(undefined)

export const useScale = (): ScaleContextType => {
	const context = useContext(ScaleContext)
	if (!context) {
		throw new Error('useScale must be used within a ScaleProvider')
	}
	return context
}

interface ScaleProviderProps {
	baseWidth: number
	baseHeight: number
	children: ReactNode | ReactNode[]
}

export const ScaleProvider = (props: ScaleProviderProps) => {
	const { baseWidth, baseHeight, children } = props
	const [scale, setScale] = useState(1)

	useEffect(() => {
		const handleResize = () => {
			const scaleWidth = window.innerWidth / baseWidth
			const scaleHeight = window.innerHeight / baseHeight
			setScale(Math.min(scaleWidth, scaleHeight))
		}

		handleResize()
		window.addEventListener('resize', handleResize)
		return () => window.removeEventListener('resize', handleResize)
	}, [baseWidth, baseHeight])

	return (
		<ScaleContext.Provider value={{ scale }}>
			<div
				style={{
					transform: `scale(${scale})`,
					transformOrigin: 'top left',
					width: '100vw',
					height: '100vh'
				}}
			>
				<div
					style={{
						width: baseWidth,
						height: baseHeight,
						display: 'flex',
						alignItems: 'center',
						justifyContent: 'center'
					}}
				>
					{children}
				</div>
			</div>
		</ScaleContext.Provider>
	)
}
```

Now, wrap your main app component with the `ScaleProvider`:

```tsx
// src/app/App.tsx
import { DiscordContextProvider } from '../hooks/useDiscordSdk'
import { Activity } from './Activity'
import { ScaleProvider } from './ScaleProvider'
import './App.css'

export default function App() {
	return (
		<DiscordContextProvider>
			<ScaleProvider baseWidth={1280} baseHeight={720}>
				<Activity />
			</ScaleProvider>
		</DiscordContextProvider>
	)
}
```

This `ScaleProvider` component will scale its children based on the user's screen size. It calculates the scale factor by dividing the screen width and height by the base resolution. It then applies this scale factor to the content using the `transform` property. Transforms run on the GPU, so they're very efficient and won't cause performance issues!

If you need to reference the scale factor in your components, you can use the `useScale` hook.

```tsx
const { scale } = useScale()
```

## Conclusion

Now, your activity will scale up or down based on the user's screen size, providing a consistent experience for everyone. You can design your activity for the base resolution and trust that it will look great on any screen!

Don't forget to **[join our Discord server](https://roboplay.dev/discord)** to chat with other developers, ask questions, and share your projects. We're here to help you build amazing apps with **Robo.js**! 🚀

{% cta https://roboplay.dev/discord %} 🚀 **Community:** Join our Discord Server {% endcta %}

Our very own Robo, **Sage**, is there to answer any questions about Robo.js, Discord.js, and more!

{% embed https://roboplay.dev/discord %}

{% embed https://dev.to/waveplay/how-to-add-multiplayer-to-your-discord-activity-lo1 %}