Two Truths and One Lie (Reddit App)
Inspiration
When it comes to Reddit, one of the biggest draws is the community. Therefore, I really wanted to create a game that would thrive in this format—a community-based game. All the popular apps on Reddit are community-driven: think r/place, r/pixelary, r/riddonkulos, or community apps like the r/wallstreetbets stock predictions. I needed a game that:
- I could reasonably build with Bolt
- Requires community participation
- Gives posters real satisfaction when users interact with their posts
That's how I ended up searching Reddit for people playing "Two Truths and One Lie" games. I found many examples of people playing or discussing the game across different subreddits, but surprisingly, I didn't find a dedicated subreddit for it. The genre did have two small subreddits (<200 members), but when exploring them, it became clear that the format using text posts just didn't work for Reddit. Only the first person to comment got to play, and posters didn't get the satisfaction of knowing who couldn't believe their unbelievable facts.
I decided to fill this void in Reddit and create Reddit's first real attempt at combining what makes Reddit amazing—stories, confessions, and silliness—in the form of a game. It's the gamification of r/confessions and r/stories.
The "Two Truths One Lie" game draws its inspiration from the classic party game, reimagined for the dynamic environment of Reddit communities. The core idea was to create an engaging and interactive experience that harnesses Reddit's unique features, allowing users to generate content, compete, and interact in a fun, deceptive way.
What it does
"Two Truths One Lie" is a complete game built entirely within the Reddit platform. It empowers users to:
Play Games Users can discover game posts within their community, read three statements (two truths and one lie), and guess which one is the false statement. They earn experience points (XP) for participating and bonus XP for making correct guesses.
Create Games Players who wish to contribute can create their own game posts using a dedicated, user-friendly form. They craft two genuine statements and one convincing lie, with the option to add extra details to make the truths more believable. Creators earn "liar points" when other players fail to identify the lie in their posts, and they receive bonus XP if their post garners 5 or more guesses.
A Full Leveling System to Instill Progress Within the Community The game features a comprehensive level system, spanning 9 distinct levels from "Huge Clown" to "Carnival Legend." Players accumulate XP to advance through these ranks. A dedicated section in the community hub allows users to monitor their current level, XP, and progress toward the next achievement.
Compete on Leaderboards The game includes both weekly and all-time leaderboards. These track "Best Guessers" (based on correct guesses) and "Best Liars" (based on how many players they've successfully fooled). Weekly leaderboards reset every Monday, encouraging ongoing competition.
Dynamic User Flairs To showcase a player's achievements, their user flair is automatically updated. This flair displays their current level, XP, and weekly guesser rank (for example, "Truth Master | 160 XP | #3"). These flairs are refreshed hourly for active participants.
Receive Notifications Players receive congratulatory private messages when they level up. These messages provide details about their new rank, current standing on the leaderboards, and helpful tips for continued progression.
For Community Moderators The application offers convenient menu actions:
- Install the Game: This action sets up the initial community hub post, which is then pinned to the subreddit. This hub serves as the central point for leaderboards and game statistics. It also automatically schedules recurring tasks for flair updates and leaderboard resets.
- Create New Game Posts: Moderators can easily create new game posts for the community.
- Manage Data: The app includes tools for testing, such as adding or clearing sample leaderboard data, and the ability to manually trigger flair updates.
How I built it
The application is built entirely on the Reddit Devvit platform, utilizing its core capabilities:
Devvit's Built-in UI System The entire user interface within Reddit posts, including gameplay, results, leaderboards, and level progress, is crafted using Devvit's native components, ensuring a seamless experience directly within the Reddit app.
Integrated Data Storage All game data, such as game posts, user scores, guesses, game settings, and leaderboard information (both weekly and all-time), is securely stored and managed using Devvit's integrated data persistence features.
Automated Task Management Recurring tasks like weekly leaderboard resets and hourly updates for user flairs are handled automatically by Devvit's scheduling system.
Interactive Web Form I hit a limitation with Reddit native components pretty quickly—no character limits! So I ended up combining Devvit native blocks and custom webviews to create a custom form for users to allow for proper character limits while filling out the form to avoid a poor submission experience. This form communicates with the main application to submit game details for post creation.
Why a character limit? Due to limitations of working within the Reddit environment, you can only display so much text before your Reddit app starts overflowing and being hidden by the UI. And you can't add a scroll feature outside of webviews (when the app pops out)! So if I wanted a seamless experience within Reddit and in the flow of scrolling for users, I needed to add character limits to ensure text displayed nicely across desktop and mobile.
User-Attributed Content The game leverages a feature that allows posts and community content to be attributed directly to the user who creates them, rather than the application itself, allowing the user to know when someone comments on their post!
Note: If you visit the subreddit, you might notice this feature not working. This is because only officially published apps can post for the user, and I'm in the waiting process to get officially reviewed and published.
Challenges we ran into
Building on a new platform like Devvit presented several unique challenges:
User Interface Design Creating responsive and information-rich layouts within the platform's native UI system required careful design and iterative adjustments to ensure a great user experience across different screen sizes. Since Devvit apps don't have a scroll feature, text and UI elements tend to go off-screen. So I had to iterate with Bolt constantly to ensure that the game worked great on any screen size.
Data Synchronization Seamlessly managing game information and user interactions between the core application logic and the interactive web form presented a challenge in ensuring data consistency. Bolt also consistently hallucinated Redis commands, so I had to consistently get it back on track with the help of Reddit AI responses explaining certain features and having it reference open-source Devvit app examples like Pixelary.
Leaderboard Accuracy Developing precise ranking algorithms for leaderboards, particularly for categories where a zero score should not be ranked, required careful implementation to ensure fairness and accuracy.
Automated User Updates Ensuring that user profiles and visual indicators (like flairs) accurately reflected real-time changes in their progress and rank, and managing these updates efficiently for many users, was a complex task.
Troubleshooting Identifying and resolving issues across different parts of the application, especially when they involve interactions between the core platform and external web components, required thorough investigation.
Platform Specifics Adapting to the unique operational environment of the platform, which has certain limitations (such as restricted access to system resources), meant rethinking standard development approaches.
Accomplishments that we're proud of
Fully Functional Game Loop We successfully implemented the entire game flow, from creation and guessing to results and progression, all operating seamlessly within the Reddit ecosystem.
Robust Progression System We developed a comprehensive level and experience point system, complete with dynamic user flairs and automated level-up notifications.
Scalable Leaderboards We created both weekly and all-time leaderboards for guessers and liars, designed to efficiently handle a growing number of players and entries.
Seamless Web Form Integration We successfully integrated an interactive web form for a rich game creation experience, demonstrating effective communication between different parts of the application.
Automated Maintenance We implemented scheduled tasks for weekly leaderboard resets and hourly flair updates, significantly reducing the need for manual moderation.
Responsive User Interface We achieved a highly responsive user interface that adapts well to various screen sizes within the Reddit app, providing a consistent and enjoyable experience for all users.
User-Generated Content We empowered users to create their own game content, fostering community engagement and ensuring a continuous stream of new and entertaining games.
What we learned
Deep Dive into Devvit We gained extensive experience with the platform's core capabilities, including its data storage, background task management, and UI system for building interactive experiences.
Cross-Environment Communication We mastered patterns for secure and efficient communication between the core application and external web components.
Optimizing for Performance We learned to optimize data retrieval and user interface rendering within the platform's operational constraints to ensure a smooth user experience.
Importance of Data Structure We understood the critical role of a well-designed data structure for efficient storage and retrieval of complex game information.
Iterative Design The development process highlighted the importance of iterative design and development, especially when working with a new platform and its unique user interface approaches.
Community-Centric Development We reinforced the value of building features that encourage user participation and content generation, which are essential for a thriving Reddit community.
What's next for Two Truths and One Lie (Reddit app)
More Game Modes Using this game as a basis, I can introduce a few more similar text and choice-style community games like "Would You Rather" and "Two Lies and a Truth" that I believe would fit well into the Reddit ecosystem.
Moderator Tools Expansion We plan to develop more advanced moderation tools, including content flagging, automated detection of inappropriate statements, and the ability to feature top-performing game posts from the liar's leaderboard.
Themed Events We envision organizing seasonal or community-wide events with special leaderboards, unique rewards, and limited-time game modes.
Build with the Community As a new community game, I've only gotten some engagement, and there's still potential for bugs or user experience fixes we can make as we get more feedback and grow the community. Even with early testing, I found a handful of bugs impacting the leaderboard a day before the submission deadline.
Conclusion
I had acgreat experience over the last two weeks creating this game. Initially, I didn't think I'd be able to finish—I ran into numerous challenges figuring out how to get Bolt to work effectively with Devvit. The complexity of having it work on a Devvit app while simultaneously managing a separate React app for creating webviews proved confusing, as Bolt would constantly modify the React components unexpectedly.
However, as I learned more about Devvit while working with Bolt, I realized I needed to modify the starter template provided by the Reddit team. My app wasn't going to be purely built on their experimental web version using React, so once I figured that out, I was truly able to collaborate with Bolt to create something silly.
I also want to thank the Bolt team for providing everyone with 20 million free tokens during the final weekend. I ended up using every single one to make my game fully featured and polished.
Technical Deep Dive
For those curious about the technical implementation, or for developers who encountered challenges using Bolt to create a Devvit app, I asked Bolt to detail the technical aspects it was most proud of:
The "Two Truths One Lie" Reddit app showcases several key technical achievements, leveraging Devvit's unique capabilities to deliver a rich, interactive, and fully integrated game experience directly within the Reddit platform.
1. Full-Stack Game on Reddit: Devvit Custom Post Types & UI
A core technical highlight is the ability to run an entire interactive game, from gameplay to leaderboards, directly within a Reddit post. This is achieved through Devvit's custom post types and its JSX-like UI framework. This approach eliminates the need for external web servers or complex client-side deployments, making the game feel native to Reddit. The Router component dynamically renders different game interfaces (e.g., game play, results, leaderboards) based on the post's state and type.
// src/devvit/main.tsx
Devvit.addCustomPostType({
name: 'ttol',
height: 'tall',
render: (context) => {
return (
<Router context={context} />
);
},
});
// src/devvit/posts/Router.tsx (simplified)
export const Router = ({ context }: RouterProps): JSX.Element => {
// ... logic to determine postType (e.g., 'game', 'pinned') ...
const content = postType === 'pinned'
? <PinnedPost context={context} /> // Community Hub
: <GamePost context={context} />; // Individual game instance
return (
<blocks height="tall">
<CarnivalBackground>
<vstack width="100%" height="100%" overflow="scroll">
{content}
</vstack>
</CarnivalBackground>
</blocks>
);
};
2. Robust Data Persistence: Leveraging Devvit's Redis
All game data, including individual game posts, user scores, guesses, and leaderboard entries, is stored persistently using Devvit's integrated Redis client. This ensures real-time updates and data integrity across user sessions without requiring an external database. The GameService class encapsulates all Redis interactions, providing a clean API for data management.
// src/devvit/service/GameService.ts
export class GameService {
constructor(private redis: any) {}
async createGamePost(gamePost: GamePost): Promise<void> {
const key = `game_post:${gamePost.postId}`;
await this.redis.set(key, JSON.stringify(gamePost));
}
async getUserScore(userId: string): Promise<UserScore> {
const key = `user_score:${userId}`;
const data = await this.redis.get(key);
return data ? JSON.parse(data) : { /* default UserScore structure */ };
}
async updateLeaderboards(userScore: UserScore): Promise<void> {
const weekNumber = this.getWeekNumber(); // Calculates current week for weekly leaderboards
await Promise.all([
// Update weekly guesser and liar leaderboards (sorted sets)
this.redis.zAdd(`leaderboard:guesser:weekly:${weekNumber}`, {
member: userScore.userId,
score: userScore.weeklyGuesserPoints
}),
this.redis.zAdd(`leaderboard:liar:weekly:${weekNumber}`, {
member: userScore.userId,
score: userScore.weeklyLiarPoints
}),
// Update all-time guesser and liar leaderboards
this.redis.zAdd('leaderboard:guesser:alltime', {
member: userScore.userId,
score: userScore.guesserPoints
}),
this.redis.zAdd('leaderboard:liar:alltime', {
member: userScore.userId,
score: userScore.liarPoints
}),
]);
}
}
3. Automated Background Tasks: Devvit's Scheduler for Dynamic Content
Critical maintenance and dynamic content updates, such as weekly leaderboard resets and hourly user flair updates, are fully automated using Devvit's built-in scheduler. This ensures the game remains fresh and accurate without manual intervention, providing a seamless experience for both players and moderators.
// src/devvit/main.tsx (scheduling jobs during game installation)
Devvit.addMenuItem({
label: '[TTOL] Install Game',
location: 'subreddit',
forUserType: 'moderator',
onPress: async (_event, context) => {
const { scheduler } = context;
// ... other installation logic ...
// Schedule hourly flair updates
const nextHour = new Date();
nextHour.setHours(nextHour.getHours() + 1, 0, 0, 0); // Next hour, on the hour
await scheduler.runJob({
name: 'UPDATE_WEEKLY_FLAIRS',
runAt: nextHour,
cron: '0 * * * *', // Every hour on the hour
});
// Schedule weekly leaderboard reset (every Monday at 00:00 UTC)
const nextMonday = new Date();
const daysUntilMonday = (1 + 7 - nextMonday.getDay()) % 7; // Calculate days until next Monday
nextMonday.setDate(nextMonday.getDate() + daysUntilMonday);
nextMonday.setHours(0, 0, 0, 0); // Set to midnight UTC
await scheduler.runJob({
name: 'RESET_WEEKLY_LEADERBOARDS',
runAt: nextMonday,
cron: '0 0 * * 1', // Every Monday at 00:00 UTC
});
},
});
// src/devvit/jobs/resetWeeklyLeaderboards.ts (simplified job definition)
export const resetWeeklyLeaderboards = Devvit.addSchedulerJob({
name: 'RESET_WEEKLY_LEADERBOARDS',
onRun: async (_event, context) => {
const gameService = new GameService(context.redis);
const currentWeekNumber = gameService.getWeekNumber();
console.log(`Starting weekly leaderboard reset for week ${currentWeekNumber}...`);
// ... logic to iterate through users, reset weekly scores, and update flairs ...
console.log(`Weekly leaderboard reset complete for week ${currentWeekNumber}`);
},
});
4. Seamless Webview Integration for Rich User Forms
To provide a richer, more interactive form experience than native Devvit UI components alone, the "Create Game" form is implemented as a separate React-based webview. This webview communicates seamlessly with the Devvit app for data submission and post creation, demonstrating effective cross-environment interaction.
// src/devvit/components/CreateGameInterface.tsx
export const CreateGameInterface = ({ context, onShowToast }: CreateGameInterfaceProps): JSX.Element => {
const { postId, userId, reddit, ui } = context;
const { mount, unmount, postMessage } = useWebView({
url: 'index.html', // Path to the React webview application
onMessage: async (message, webView) => {
if (message.type === 'webViewReady') {
// Send initial data (e.g., current user) to the webview when it's ready
webView.postMessage({
type: 'INIT_DATA',
data: {
postId,
userId,
authorUsername: (await reddit.getCurrentUser())?.username
}
});
} else if (message.type === 'CREATE_GAME_SUBMIT') {
// Receive submitted form data from the webview
const { truth1, truth2, lie } = message.data;
// ... logic to create a new Reddit post with game data ...
webView.unmount(); // Close the webview after submission
ui.showToast('Game post created successfully! 🎪');
}
},
onUnmount: () => console.log('Webview closed'),
});
return (
<button onPress={mount}>Create Post 🎪</button> // Button to open the webview
);
};
5. Dynamic Leaderboard Ranking and User Flair Management
The application features sophisticated logic for calculating and maintaining user ranks across multiple leaderboards (weekly/all-time, guesser/liar). This involves efficiently querying sorted sets in Redis and dynamically updating user flairs on Reddit to reflect their real-time achievements and level progression, providing a visible representation of their in-game status.
// src/devvit/service/GameService.ts
export class GameService {
// ...
async getLeaderboard(
type: 'guesser' | 'liar',
timeframe: 'weekly' | 'alltime',
limit: number = 10
): Promise<LeaderboardEntry[]> {
const weekNumber = this.getWeekNumber();
const key = timeframe === 'weekly'
? `leaderboard:${type}:weekly:${weekNumber}`
: `leaderboard:${type}:alltime`;
// Retrieve top entries from Redis sorted set
const results = await this.redis.zRange(key, 0, limit - 1, {
withScores: true,
reverse: true
});
// ... process results to include usernames and calculate ranks ...
return leaderboard;
}
async updateUserFlair(username: string, subredditName: string, reddit: any): Promise<void> {
try {
const userScore = await this.findUserByUsername(username); // Retrieve user's full score data
if (!userScore) return;
const levelInfo = this.getLevelByExperience(userScore.experience); // Get current level details
const weeklyGuesserRank = await this.getUserLeaderboardRank(
userScore.userId,
'guesser',
'weekly'
); // Get user's weekly rank
const rankText = weeklyGuesserRank ? `#${weeklyGuesserRank}` : 'Unranked';
// Construct dynamic flair text: "Level Name | XP | Weekly Rank"
const flairText = `${levelInfo.name} | ${userScore.experience} XP | ${rankText}`;
await reddit.setUserFlair({
subredditName,
username,
text: flairText,
backgroundColor: this.getLevelFlairColor(levelInfo.level), // Dynamic color based on level
textColor: 'dark', // Ensure readability
});
} catch (error) {
console.error(`Error updating flair for u/${username}:`, error);
}
}
}
This technical implementation demonstrates the power of Devvit's platform for creating sophisticated, interactive experiences that feel native to Reddit while maintaining the scalability and reliability expected from modern web applications.
Built With
- bolt.new
- css
- devvit
- react
- redis
- typescript
Log in or sign up for Devpost to join the conversation.