Discord Activities 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!
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:
After scaling, content fits the screen and is fully visible:
Create a Discord Activity Project
Create a Discord Activity project if you don't have one already:
npx create-robo my-activity -k activity
We'll be using React and TypeScript for this example, but you can apply the same principles to any frontend framework or vanilla HTML and CSS.
We highly recommend using create-robo to create a Discord Activity project. Robo.js providers a lot of features and tools to help you build your activity faster and more efficiently, such as Multiplayer Support, Easy Hosting, Streamlined Tunnels, Built-in Database, Plugin System, and so much more!
Creating a Discord Activity in seconds with Robo.js
WavePlay Staff for WavePlay ・ Apr 9
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 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:
// 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
:
// 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.
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 to chat with other developers, ask questions, and share your projects. We're here to help you build amazing apps with Robo.js! 🚀
🚀 Community: Join our Discord Server
Our very own Robo, Sage, is there to answer any questions about Robo.js, Discord.js, and more!
Top comments (1)
Very cool post, thanks for sharing!