-
-
Tap notes onto the same grid the dance uses. Up to 6 tracks, 90-160 BPM, or shuffle a pentatonic pattern that always lands in key.
-
Every dance plays as you scroll — each one posed, drawn, and scored by the person who made it. React with emoji, or with a dance.
-
Drag any of 11 joints to pose a keyframe. Limbs have fixed lengths, so arms swing on a circle instead of stretching. Frames ease between.
-
Draw the stage yourself — one backdrop for the whole dance, or a different one per frame so the scene moves with the dancer.
Inspiration
I started my career at a mobile game company and stayed for about ten years. I don't work at a game company anymore.
The last game I shipped there was called Danceville. It was a good game. It didn't succeed. That's the combination that stays with you — I always wanted to rebuild it my way, and I never did. It just sat there.
Then, one lunch with coworkers, almost everyone at the table was talking about their kids. I heard something that ten years in games had somehow hidden from me: none of them felt good about games. They would rather their kids play sports, or do almost anything else. But piano lessons and art classes were different. Those felt creative, expressive, and worth supporting.
And I thought: wait. I already made that game.
VibeStep has music in it. It has drawing in it. It also has the part people actually want after making something: showing it to someone they trust and hearing that they did well. I wanted sharing to carry that feeling.
But sharing also invites noise, arguments, and words written without much thought. I wondered whether giving people fewer chances to speak might make each message more intentional. So VibeStep limits text comments to three per twelve hours, while visual reactions remain open.
That's where VibeStep came from.
What it does
VibeStep is a dance maker where motion, art, and music share the same timeline. You pose a dancer frame by frame, draw the stage, and build a beat that moves with the animation.
- Motion. Drag the joints of a 2D figure to set each keyframe pose. Arms and legs have fixed lengths, so a hand pulled away from its shoulder swings along a circle instead of stretching. Ease-in-out interpolation connects the poses, so even four keyframes read as movement rather than a flipbook. Creators can expand a dance from four frames to eight and then sixteen.
- Art. Doodle directly on the stage, add reusable shapes, or draw a different backdrop for each frame so the scene changes with the dance.
- Music. Arrange up to six instrument tracks using piano, violin, viola, synth, guitar, bass, cello, flute, trumpet, kick, clap, and hi-hat. Creators can tap in their own notes from 90 to 160 BPM or shuffle a pentatonic pattern that stays musical.
A finished dance can be saved to a personal library and published as a public or private post. The feed plays dances as you scroll. People can react with multiple emoji or reply with a dance of their own: a move from their library becomes a motion emoji they can leave on someone else's post. Visual reactions have no cooldown. Only text does.
Creators can also export a dance as a GIF or share it as an MP4 video with its music. Motion, music, backgrounds, and doodle shapes can be saved as reusable templates, making it faster to build the next dance without starting over.
Coins unlock longer animations, more library and template slots, per-frame backdrops, outfits, instruments, expressions, and the ability to use personal dances as reactions. On mobile, players can purchase coin packs or earn 50 coins from a rewarded ad once per day.
How I built it
VibeStep is a Flutter client and a Go server in one repository. It uses Firestore for data, Cloud Run for the API, Firebase Hosting for the web client, and Cloud Build for deployment.
Dart and Go cannot share a line of code, so the repository has a contract/
directory that holds the real shape of every request and response. It is the
single source of truth: when a response changes, the client, server, fixtures,
and contract change together. That rule kept a solo project spanning two
languages and multiple platforms from quietly drifting apart.
Purchases use the RevenueCat SDK, but the client never grants currency. When a purchase finishes, the app only asks the server to refresh the balance. RevenueCat sends the transaction to a webhook, and the server validates the event, identifies the product and user, records an idempotent grant, and then updates the balance.
If the client could say, "I paid, give me coins," one forged request could mint unlimited currency. Because the webhook can arrive a few seconds after the store reports success, the app has an explicit pending state and retries the balance refresh instead of pretending the coins are already there.
I later applied the same trust rule to rewarded ads. Watching an ad does not directly grant anything on the device. The app requests a short-lived session bound to the signed-in user, AdMob sends a server-side verification callback, and the server verifies its signature and transaction ID before granting the daily reward. Paid and earned currency therefore follow the same principle: external proof is verified by the server, and every grant is recorded exactly once.
The social features follow the same defensive approach. Comment limits are enforced with Firestore transactions, not local timers. Push notifications are rate-limited so reactions cannot become notification spam. Public content is protected with reporting, blocking, profanity filtering, and full in-app account deletion.
Challenges I ran into
The purchase trust boundary. Moving the grant to a RevenueCat webhook was
the easy half. The hard half was every place where that asynchronous boundary
became visible: the webhook arriving late, product identifiers carrying
purchase-option suffixes such as coins_1000:base, duplicate callbacks, and
deciding what the interface should say while the store and server temporarily
disagreed.
Turning a dance into a shareable video. The animation, frame backgrounds, and generated music all run on their own timelines inside the app. Exporting them meant rendering frames off-screen, generating audio, encoding them into an MP4, and keeping the beat aligned with the motion. The result also had to work across Android and iOS rather than only inside Flutter's on-screen preview.
Shipping alone on two mobile stores. Store review became a larger bottleneck than writing code. Along the way I found authentication tokens being included in device backups, temporary debug output that had no place in a production build, and a dance parser that failed when coordinates were serialized as integers instead of decimals. Shipping on iPhone also meant handling Apple sign-in, StoreKit products, push notification permissions, and platform-specific media behavior without splitting the product into two different apps.
Making a creation tool fit a phone. Almost everything is controlled by dragging and tapping; the keyboard is reserved for titles and comments. The figure rescales to the canvas so heads and feet do not clip on small screens. Editors, timelines, instrument controls, and wardrobe tabs also had to remain usable when translated text became much longer than the original English. Arabic required the interface to work correctly from right to left rather than simply replacing the words.
Accomplishments that I'm proud of
- It actually shipped. I launched VibeStep solo on Google Play on September 4, 2026, and then brought it to the Apple App Store for iPhone and iPad before the Shipaton deadline.
- Motion, art, and music genuinely work together as one creation system instead of feeling like three separate tools attached to the same app.
- A dance can leave the app as a GIF or an MP4 with music, while its motion, music, and artwork can be saved as reusable building blocks inside the app.
- Both paid purchases and rewarded-ad currency are server-authoritative and idempotent by design, rather than being secured after the fact.
- The project supports more than twenty locale variants, including both Chinese scripts and a full right-to-left layout for Arabic.
- Reporting, blocking, profanity filtering, notification throttling, and full in-app account deletion were treated as launch requirements rather than postponed moderation work.
What I learned
The most useful thing anyone told me about this game came from a lunch table, not a design document. I had been thinking only about players. The conversation that changed the project was about the people who decide whether an experience feels worth inviting into their lives.
I also learned that a constraint can become the feature people remember. The comment cooldown is the only part of VibeStep intentionally designed to do less than it technically could. That limitation communicates the kind of community I want more clearly than another list of features would.
Building the monetization system taught me a similar lesson. A successful purchase screen is only the visible beginning. The real product includes late webhooks, duplicate events, interrupted sessions, refunds, stale balances, and the moment when a player has paid but the interface cannot yet prove it. Making those states explicit produced a more trustworthy experience than hiding them behind optimistic UI.
Finally, launching on both Android and iOS reminded me that shipping is not the end of development. Real devices, store policies, translations, first-time users, and actual network delays expose assumptions that local development never does.
What's next for VibeStep
I'm going to keep building VibeStep regardless of how Shipaton goes.
Now that it is live on both major mobile stores, the next challenge is making the first session teach itself. I want to improve onboarding, give new users a stronger starting feed, and make the growing motion, music, and doodle template libraries even faster to use.
Most importantly, I want the first Android and iOS creators to shape what comes next. VibeStep began with an old game I could not stop thinking about, but its future should come from watching what people actually make with it.
Built With
- android
- cloud-run
- dart
- firebase
- firebase-authentication
- firebase-hosting
- firestore
- flutter
- gin
- go
- google-cloud
- google-play
- in-app-purchase
- rest-api
- revenuecat
- webhooks
Log in or sign up for Devpost to join the conversation.