Hack Reddit 2025 Event
Today marks the end of the Hack Reddit 2025 Event over on Devpost. It's been a grueling few weeks with more all-nighters than I can even count at this point, but I got my entry submitted with a whole 13 mins to spare. And that was only by the grace of a major Reddit outage yesterday that caused them to extend the deadline. Otherwise, I would not have been ready. And that would have been a real shame. Because I think I might actually have a chance to win the grand prize. Now of course, everyone likes to think that about their own projects. This article is meant to show exactly what sets my submission apart from every entry that I've seen so far. I know the concept of my game is silly. But it's not the content that is important here. What's special is HOW it works. You see, Devvit is not your typical Web API or Framework. It's a custom React-like runtime environment that they've crafted which has some very specific limitations. Custom Interactive Posts are rendered on the server first, then the context switches to the browser but none of the usual browser APIs are available. There is no requestAnimationFrame, no InputEvent handling, essential none of the typical realtime features you would expect on the web. Not even setTimeout is available, but execution time is limited to 1s total per render anyway so it wouldn't do any good if it was. Trial and Error Posts are typically built from blocks. But there is only so much you can do with the built in ones. However, with a little creativity and a LOT of persistence I came up with a way to achieve full 60FPS rendering. Now, before I explain any further. Keep in mind that this was a hackathon entry and not a production app. This method is not even slightly efficient. But it was the ONLY way that I found to do get animated content to render apart from using animated gifs. The trick was to build an SVG template literal out of PNGs packed into Data URLs and manipulate this template on a loop. Believe me, if there was literally ANY other way to do this I would have jumped on it... Cloning Data URLs inside an SVG string got bloated pretty darn fast. I probably could have optimized a bit more but I was running out of time so it is what it is. I had to limit the number of elements on screen at any one time and limit the framerate to 30fps to get any kind of reasonable performance. It can lag a bit here and there but it is the ONLY example of live animation that I have seen. What Makes it Tick The core feature of Devvit that makes this possible is useInterval. According to the docs, this is limited to intervals of 1s minimum. However, I discovered that limitation only applies on the server side. If you manipulate the component loading just right, you can wait until the component is fully mounted on the client side before starting the loop. Here's a pretty rough template showing how I made it work. Be aware that this is a NOT A FULLY WORKING EXAMPLE but a very stripped down snapshot of the moving parts that make my game possible. If anyone has any questions about this method, leave a comment and I will try and elaborate. render: context => { const [isClient, setIsClient] = useState(false); const [avatarUrl, setAvatarUrl] = useState('data:image/png;base64,';); const [imgSrc, setImgSrc] = useState(fallbackImg); function svgTemplate(content: string) => { const svg = ` ${content} `.trim().replace(/\s+/g, ' '); return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`; } function getSprite(sprite: SpriteState) => { } function buildImage(state: GameState) { const output = getSprite(state.player); // output += ... generate more sprite or svg elements return output; } const { data: message, loading, error } = useAsync(async () => { return fetch(userData.avatar) .then(res => res.arrayBuffer()) .then(buffer => buffer.toString('base64')); },{ depends: [isClient], finally: result => { setAvatar(`data:image/png;base64,${result}`); gameLoop.start(); } }); const gameloop = useInterval(() => { // do physics calculations here setImgSrc(svgTemplate(buildImage(gameState))); }, 33); useState(() => { setIsClient(true); return null; }); return ( ); } Disclaimer This method does NOT currently work in the Reddit mobile app. The game loop will run but there is heavy flickering and, at least on Android, the SVG failed to render when it had Data URLs embedded in it. Aside from the flicker, it did render at high FPS with only SVG shapes like in the Playground link above.

Today marks the end of the Hack Reddit 2025 Event over on Devpost. It's been a grueling few weeks with more all-nighters than I can even count at this point, but I got my entry submitted with a whole 13 mins to spare. And that was only by the grace of a major Reddit outage yesterday that caused them to extend the deadline. Otherwise, I would not have been ready. And that would have been a real shame. Because I think I might actually have a chance to win the grand prize.
Now of course, everyone likes to think that about their own projects. This article is meant to show exactly what sets my submission apart from every entry that I've seen so far. I know the concept of my game is silly. But it's not the content that is important here. What's special is HOW it works.
You see, Devvit is not your typical Web API or Framework. It's a custom React-like runtime environment that they've crafted which has some very specific limitations. Custom Interactive Posts are rendered on the server first, then the context switches to the browser but none of the usual browser APIs are available. There is no requestAnimationFrame
, no InputEvent
handling, essential none of the typical realtime features you would expect on the web. Not even setTimeout
is available, but execution time is limited to 1s total per render anyway so it wouldn't do any good if it was.
Trial and Error
Posts are typically built from blocks. But there is only so much you can do with the built in ones. However, with a little creativity and a LOT of persistence I came up with a way to achieve full 60FPS rendering. Now, before I explain any further. Keep in mind that this was a hackathon entry and not a production app. This method is not even slightly efficient. But it was the ONLY way that I found to do get animated content to render apart from using animated gifs.
The trick was to build an SVG template literal out of PNGs packed into Data URLs and manipulate this template on a loop. Believe me, if there was literally ANY other way to do this I would have jumped on it...
Cloning Data URLs inside an SVG string got bloated pretty darn fast. I probably could have optimized a bit more but I was running out of time so it is what it is. I had to limit the number of elements on screen at any one time and limit the framerate to 30fps to get any kind of reasonable performance. It can lag a bit here and there but it is the ONLY example of live animation that I have seen.
What Makes it Tick
The core feature of Devvit that makes this possible is useInterval
. According to the docs, this is limited to intervals of 1s minimum. However, I discovered that limitation only applies on the server side. If you manipulate the component loading just right, you can wait until the component is fully mounted on the client side before starting the loop. Here's a pretty rough template showing how I made it work. Be aware that this is a NOT A FULLY WORKING EXAMPLE but a very stripped down snapshot of the moving parts that make my game possible. If anyone has any questions about this method, leave a comment and I will try and elaborate.
render: context => {
const [isClient, setIsClient] = useState(false);
const [avatarUrl, setAvatarUrl] = useState('data:image/png;base64,';);
const [imgSrc, setImgSrc] = useState(fallbackImg);
function svgTemplate(content: string) => {
const svg = `
imgWidth}" height="${imgHeight}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet">
${content}
`.trim().replace(/\s+/g, ' ');
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
}
function getSprite(sprite: SpriteState) => {
<image x="${sprite.x}" y="${sprite.y}" width="${sprite.width}" height="${sprite.height}" href="${sprite.img}" />
}
function buildImage(state: GameState) {
const output = getSprite(state.player);
// output += ... generate more sprite or svg elements
return output;
}
const { data: message, loading, error } = useAsync(async () => {
return fetch(userData.avatar)
.then(res => res.arrayBuffer())
.then(buffer => buffer.toString('base64'));
},{
depends: [isClient],
finally: result => {
setAvatar(`data:image/png;base64,${result}`);
gameLoop.start();
}
});
const gameloop = useInterval(() => {
// do physics calculations here
setImgSrc(svgTemplate(buildImage(gameState)));
}, 33);
useState(() => {
setIsClient(true);
return null;
});
return (
<vstack alignment="center middle" width="100%" height="100%">
<image
url={imgSrc}
imageHeight={imgHeight}
imageWidth={imgWidth}
height="100%"
width="100%"
resizeMode="fit" />
</vstack>
);
}
Disclaimer
This method does NOT currently work in the Reddit mobile app. The game loop will run but there is heavy flickering and, at least on Android, the SVG failed to render when it had Data URLs embedded in it. Aside from the flicker, it did render at high FPS with only SVG shapes like in the Playground link above.