Avatar’s Fate: A Tarot Simulator – A Developer’s Breakdown
👩💻 The Goal
I wanted to create a UI-based tarot simulator in Roblox with:
- A shuffle and draw mechanic
- Upright/reversed card logic
- Reveal animation with meaning overlays
- No avatars, movement, or 3D interaction—just clean interface work
I gave myself a weekend to prototype and publish.
🧠 Design Philosophy: MVP + Magic
Because I had limited time, I leaned into the following principles:
- Focus on one mechanic: Draw a tarot card. That’s the core loop.
- 2D UI only: All game logic happens in the GUI layer—no 3D elements or character controllers.
- Keep art static: I used a fixed set of 22 tarot images I designed elsewhere.
🛠️ Tools Used
- Roblox Studio – Core development
- ChatGPT/ Photoshop – Card meaning descriptions / Card illustrations
- Elevenlabs.io - Text to speech AI voiceovers
- Studio Scripts – Lua, all local and module-based
- No external frameworks (yet) – MVP kept it tight
🔄 Core Systems
1. Deck & Draw Logic
Cards are stored in a ModuleScript as a table of dictionaries:
local Deck = {
{ name = "The Fool", id = "card_00", meaning = "...", reversed = "..." },
{ name = "The Magician", id = "card_01", meaning = "...", reversed = "..." },
-- etc}
A DrawCard()
function:
- Clones a card GUI template
- Randomly selects a card from the deck
- Applies a 50/50 upright/reversed orientation
- Animates into position
Cards are removed or reshuffled depending on game mode.
2. Card Reveal Animation
Reveal is handled with a TweenPosition and TweenRotation chain:
local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local flipTween = TweenService:Create(cardFrame, tweenInfo, { Rotation = 90 })
flipTween:Play()
flipTween.Completed:Connect(function()
cardImage.Image = revealImage
cardFrame.Rotation = -90
TweenService:Create(cardFrame, tweenInfo, { Rotation = 0 }):Play()
end)
Simple, smooth, and mobile-safe.
3. Upright vs Reversed Meaning Overlay
When the card is drawn, a text overlay fades in with either the upright or reversed meaning.
if isReversed then
meaningLabel.Text = cardData.reversed
cardImage.Rotation = 180
else
meaningLabel.Text = cardData.meaning
end
The overlay fades in based on user input timing.
4. Audio Feedback
Each card has a unique voiceover. When revealed:
local sound = Instance.new("Sound", cardFrame)
sound.SoundId = "rbxassetid://12345678" -- stored in module
sound:Play()
I fed the card meaning scripts into ElevenLabs.io, using the Old Wizard voice for narration. The result gives each card a mysterious, immersive vibe that enhances the draw experience.
🧪 Playtesting Notes
Playtesting... didn’t really happen 😅
I tested myself, iterated quickly, and made sure nothing broke. Because the experience is single-player and visual, I focused on user flow and animation timing. I had to rework the layout again once I remembered to check on mobile 😅- its not perfect but readable.
⚙️ What Worked
- Keeping it 2D-only made UI testing faster and removed camera issues
- Skipping avatars let me ship faster and focus on storytelling
💡 What’s Next
- A daily card draw system (reward hook + longer engagement)
- A meta-system for readings (e.g., 3-card spreads)
👩🚀 Final Thoughts
If you're a solo dev or a parent trying to ship something meaningful without losing sleep, here’s my advice:
- Design around your time budget
- Cut scope aggressively
- Prioritize flow and delight over complexity
- Know what you can layer in later
Avatar’s Fate is a small game. But it’s complete. And I built it between kid stuff, chores, and a lot of “maybe later”s.
And that feels kind of magical.
Check out Avatar's Fate: A Tarot Simulator!