DEV Community

Cover image for Achieve Game-Like Scaling in Discord Activities and Web Apps Using CSS
WavePlay Staff for WavePlay

Posted on

Achieve Game-Like Scaling in Discord Activities and Web Apps Using CSS

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!

Image

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

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

Image

Create a Discord Activity Project

Create a Discord Activity project if you don't have one already:

npx create-robo my-activity -k activity
Enter fullscreen mode Exit fullscreen mode

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!

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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!

Robo - Imagine Magic

Power up Discord with effortless activities, bots, servers, and more! ⚡ | 41 members

favicon discord.com

Top comments (1)

Collapse
 
thomasbnt profile image
Thomas Bnt ☕

Very cool post, thanks for sharing!