Community

Apps

Zappy Bird

Zappy Bird makes every serious NWC warning feel wonderfully concrete: connect a wallet, tap a bird through pipes, and watch tiny Lightning payments become part of the game loop.

Zappy Bird visual
Zappy Bird icon
Apps The product layer Clients, signers, publishing tools, wallets and useful experiments.
Back to Nostr
Apps

Apps shelf

Apps pages collect clients, signers, tools, developer libraries and product research without turning the app into the whole network.

Apps31 min readLightning arcade game using Bitcoin Connect, NWC wallet permissions and one-sat payment loops

Zappy Bird

Zappy Bird makes every serious NWC warning feel wonderfully concrete: connect a wallet, tap a bird through pipes, and watch tiny Lightning payments become part of the game loop.

The quick readZappy Bird is a small but useful Nostr Wallet Connect example because it puts payments inside a fast browser game instead of a checkout form. The project is a TypeScript fork of an open Flappy Bird clone, published at `rolznz.github.io/zappy-bird/` by Roland. The app initializes Bitcoin Connect with the `nwc` filter, asks for `get_balance` and `pay_invoice`, shows wallet balance, and configures a monthly budget with a code comment of 10k sats. When a wallet connects, the provider is assigned to `window.webln`, so the game can use a WebLN-style `sendPayment` call backed by NWC. The actual play loop requests a 1 sat invoice from `zappybird2@getalby.com` through Alby Lightning Tools, sends that invoice through the connected wallet, and increments the score only when a preimage comes back. The code also uses a separate hard-coded NWC URL to read the prize-pool balance through `NostrWebLNProvider.getBalance()`. Treat it as a payment demo with a game attached: use a small capped NWC connection, check the permissions, expect rough edges, and do not connect a high-balance wallet just to chase a high score.

A tiny game that makes NWC visible

Zappy Bird is not trying to be a social client, a wallet, a relay, a marketplace or a grand protocol layer. It is a browser game that makes one idea very easy to feel: if a web app can request Lightning payments through Nostr Wallet Connect, then even a tap-heavy arcade loop can become a payment surface.

The live app is hosted on GitHub Pages at `rolznz.github.io/zappy-bird/`. The repository is `rolznz/zappy-bird`, a fork of `iamaliybi/flappy-bird`, with TypeScript, canvas assets, Webpack and Alby payment libraries layered onto a simple Flappy Bird style game. The README calls it a Lightning powered fork and links directly to the playable page.

The game itself is familiar. A bird moves right, pipes scroll toward it, gravity pulls it down and the player taps or presses a key to jump. What changes the meaning of the game is the payment loop. After the first movement and once the bird has entered the pipe section, the app starts requesting tiny Lightning payments while play continues.

That makes Zappy Bird useful for the Crays Apps hub even if it is small. It is a compact demonstration of NWC as product infrastructure. Instead of asking the reader to imagine automated app payments, it gives you a silly, low-stakes place to inspect wallet permissions, budgets, invoices, balance display and the gap between a WebLN interface and an NWC-backed wallet.

What the project actually is

The public repository metadata says the project is a fork. That matters because much of the game surface comes from the original Flappy Bird clone: player physics, pipes, canvas drawing, background assets and a local best score. Zappy Bird is not a new game engine. It is a payment experiment placed inside a proven arcade template.

The repo was created in August 2023, uses TypeScript as its main language, has a GitHub Pages homepage, and had its main branch pushed in October 2025 when checked for this article. There are no GitHub releases, and GitHub issues are disabled in the repository metadata. You should treat the live page and source files as the real documentation.

The package file tells the implementation story. Runtime dependencies include `@getalby/bitcoin-connect`, `@getalby/lightning-tools`, `@getalby/sdk`, `copy-webpack-plugin` and `seedrandom`. Dev dependencies are the usual Webpack, TypeScript, Sass and loader stack. That is a web demo stack, not a mobile game stack or a server-side gaming platform.

The public HTML is also minimal. It sets the title to Zappy Bird, loads app and style CSS, links a PWA manifest, includes a Bitcoin Connect button through ``, and mounts a canvas with id `flappyBird`. The payment UX and game loop are therefore mostly inside `src/index.ts`, not spread across a large app framework.

The wallet connection is Bitcoin Connect

Zappy Bird initializes Bitcoin Connect with `appName` set to Zappy Bird, an app icon hosted from the game assets, `filters` set to `nwc`, and `showBalance` set to true. That tells you the app is deliberately pushing users toward NWC connectors rather than every possible wallet connector type.

The NWC provider config asks for `get_balance` and `pay_invoice`. It also sets `maxAmount` to `10_000_000` with a code comment of 10k sats and `budgetRenewal` to monthly. Those values are exactly the kind of detail a reader should inspect in their wallet approval screen, because this game is not asking for a one-time static invoice scan. It is asking for an app connection that can make repeated payment requests.

Bitcoin Connect is the right tool for this kind of demo because it turns wallet connection into a web component and provider flow. The player should still think of it as a permission grant. A friendly game button does not remove the need to confirm what methods, budget, renewal period and wallet account are being exposed.

Once Bitcoin Connect reports a provider through `onConnected`, the code assigns that provider to `window.webln` and flips a `weblnEnabled` flag. From the game loop's point of view, the wallet is now a WebLN provider. Under the hood, the selected connector may be an NWC connection. That bridge is the heart of the project.

How one jump becomes a payment

The actual payment path is short enough to audit. The code creates a `LightningAddress` instance for `zappybird2@getalby.com` and calls `ln.fetch()` at startup. When the game needs a payment, it asks that Lightning address for an invoice with `satoshi: 1` and a comment that includes the generated game id.

The returned invoice is sent through `window.webln.sendPayment(invoice.paymentRequest)`. If the result does not contain a preimage, the code throws an error. If the result is good, the current score increments and a lightning strike animation is added to the canvas. The high-score feeling is therefore tied to confirmed Lightning payment results, not just keypresses.

There is an important nuance. The `pay()` function is called from the jump handler but not awaited before the bird's velocity changes. In plain English, the game still lets the bird jump while the payment request happens asynchronously. If the payment fails, the score does not increment and the error is logged, but the tap itself is not visibly blocked in the same line of code.

That makes the app more playable and less strict than a naive pay-before-every-frame design. It also means readers should not over-describe the mechanic. The screen copy says 1 sat per jump once you enter the pipes. The code shows an asynchronous 1 sat payment attempt on jumps after the pipe threshold, with score credit only after a paid invoice returns a preimage.

The prize pool balance is a separate NWC read

Zappy Bird also displays a prize pool balance. This path is separate from the player's wallet connection. The code defines a hard-coded `nostr+walletconnect://` URL named `zapPoolBalanceNWCUrl` with a comment saying balance permission only. It creates a `NostrWebLNProvider`, enables it, calls `getBalance()`, stores the returned balance and closes the provider.

That is an interesting design because it uses NWC in two roles. Your own wallet connection is for playing and paying invoices. The prize-pool connection is a read path for a pool balance. The app can therefore show shared payment context without requiring every player to trust a hidden server-side balance statement.

The security lesson is obvious: a public NWC connection string should be scoped tightly. If it really only has balance permission, the exposure is mostly informational. If a future version expanded that connection's methods or budget, publishing it in front-end code would be dangerous. Hard-coded NWC secrets are only acceptable when the allowed action is intentionally public and low risk.

For readers, the practical check is simple. Treat the prize-pool number as a UI signal, not as a guarantee of payouts or contest rules. The visible game code shows balance reading and screen text about screenshots and the `#zappybird` tag. It does not show a complete tournament backend, automated winner selection or a formal payout contract.

Where Nostr enters the picture

Zappy Bird does not look like a Nostr social app. It does not publish notes, read follows, open a timeline or ask you for an npub. Its Nostr connection is Nostr Wallet Connect, the protocol path that lets an application request wallet actions through relays after the user has approved a connection.

The NWC site describes the model clearly: once an app connection is created, the app can request payments through a Nostr relay. In Zappy Bird, Bitcoin Connect handles the user-facing connection flow, and the game then sees a WebLN-like provider that can pay invoices. The Nostr part is below the surface, in the wallet-control channel.

That distinction matters because users often hear Nostr and assume social identity. Here, the useful object is not a profile or post. It is a permissioned connection between a game and a Lightning wallet. The player does not need to understand relay event kinds in order to play, but they do need to understand that an app connection can keep asking for allowed actions.

This is why Zappy Bird belongs in Games and on an NWC map at the same time. It is a game by user experience, an NWC demo by architecture, and a Lightning micropayment experiment by economics. Calling it only a Flappy Bird clone undersells the payment lesson; calling it a broad Nostr app oversells the social protocol surface.

The app asks for a budget, not custody

The healthy way to play Zappy Bird is with a deliberately limited wallet connection. NWC is designed so apps can request wallet actions without taking custody of the user's funds. That does not make every permission safe. It means the risk moves from custody to authorization: which wallet, which methods, what cap, what renewal and how to revoke.

The code requests `pay_invoice` because the game needs to pay one-sat invoices. It requests `get_balance` because the UX can show balance. Those are reasonable methods for a payment game. They are also methods you should not grant against a wallet with a large balance and no tight budget.

The configured monthly budget in the source is part of the product contract, but your wallet approval screen is the source of truth at the moment you connect. If the wallet presents a different limit, unclear renewal behavior or a broad permission set, stop there. The game is not worth overriding your own risk model.

A good setup is a small sub-wallet or NWC connection made specifically for experiments. Put in an amount you are comfortable losing to taps, bugs, failed approvals, browser refreshes or your own curiosity. Then disconnect or revoke the connection when you are done.

The game loop is intentionally rough

Zappy Bird is charming partly because it is rough. The canvas code draws background, pipes, bird, ground, score, restart states and lightning strikes. The screen can say `CONNECT WALLET TO PLAY`, `TAP TO START`, `PRIZE POOL LOADING` or a score screen with a generated id. It feels like a lab demo that became playable enough to share.

The frame loop uses `requestAnimationFrame` with a timing guard and logs a warning if the app is running slowly. The game stores the best score in `localStorage` using a tiny base-32 encoding helper. That is not anti-cheat. It is local browser state for a casual game. A serious leaderboard would need a different design.

The play rules also show the demo nature. The app starts in a still state, creates occasional lightning animations, waits for wallet connection, starts on tap and then charges after the bird reaches pipe territory. Failed payment attempts are logged to the console. The code currently has a commented-out line that would make a payment failure trigger loss, but it is not active.

That roughness is not a reason to dismiss it. For payment UX, rough demos are often more honest than polished mockups. They show where wallet latency, confirmation UX, balance permissions, browser focus and repeated payment prompts can make or break a product idea.

The most useful thing to watch is not your score. It is the rhythm between input, wallet request and visible feedback. If a wallet requires manual approval for every invoice, the game quickly becomes a prompt treadmill. If the wallet connection is too permissive, the game becomes smooth but the risk moves into the monthly cap. Zappy Bird sits exactly on that design tension.

Mobile testing deserves extra care. A canvas game, a wallet connector, a browser extension or in-app wallet view, and repeated Lightning requests can compete for focus on a small screen. A desktop browser may keep the canvas, wallet popup and console readable; a mobile browser may hide the approval context, reload state or make the player miss what was actually authorized.

Budget math is the other rough edge. One sat sounds harmless, but repeated jumps turn small permissions into real spend. A 10k sat monthly cap is still 10,000 paid score events if everything works and the wallet honors that cap as expected. The right cap for a first test is not the cap in the source code; it is the smallest cap your wallet lets you set while still proving the flow.

What to test before playing with real sats

Start with the connection modal. Confirm that the app name is Zappy Bird and that the connector path is NWC. Read the methods and budget. If the wallet hides those details, use a wallet account with almost nothing in it or skip the test. The smallest game can still teach expensive habits if you connect the wrong wallet.

Next, load the page and wait for the starting screen. If it says to connect a wallet, do that first. If it says tap to start, check that the wallet balance appears as expected. Browser extensions, mobile browsers and wallet connectors can behave differently, so do not assume a desktop success means every device is safe.

Then play one very short round. Tap into the pipe section, watch whether payment prompts appear, and check whether score increments match paid invoices. If your wallet records invoice comments, look for the Zappy Bird game id. If payments fail but the bird still moves, remember the code's asynchronous behavior and inspect the wallet log before blaming the game.

Finally, revoke the connection after the test. That is the habit Zappy Bird should teach. NWC makes repeated app payments possible, and that is powerful. Repeated permissions should also have an end. A game session should not become an abandoned authorization sitting in your wallet forever.

Developer lessons from the code

For developers, Zappy Bird is a useful reading exercise because it keeps the integration visible. `initBitcoinConnect()` configures the connection surface. `onConnected()` maps the provider to `window.webln`. `LightningAddress.requestInvoice()` creates a one-sat BOLT11 invoice. `sendPayment()` attempts to pay it. `getBalance()` reads a separate NWC balance for the prize pool.

The code also shows a realistic product tradeoff. If every tap blocked on a wallet round trip, the game could become unplayable. If payments are completely detached from the game loop, the demo loses meaning. Zappy Bird chooses an asynchronous middle path: movement remains responsive, while scoring waits for proof of payment.

A production version would need stronger state handling. It would need clearer handling for duplicate invoices, failed payments, pending payments, user cancellation, wallet disconnects, budget exhaustion and page refreshes. It would also need to decide whether a paid jump is the right unit, or whether a prepaid session or per-round fee would feel better.

That is the value of the demo. It does not answer every design question. It makes the questions concrete. Once you have watched a tiny game ask a wallet for one sat over and over, you can reason about NWC budgets, UX fatigue and revocation with much less abstraction.

What not to overclaim

Do not describe Zappy Bird as a decentralized game world. The game state is not stored on Nostr relays. The high score is local browser storage. The playable app is a GitHub Pages deployment. The Lightning address and NWC connections are payment rails, not a decentralized game backend.

Do not describe it as a wallet. It does not custody funds, generate keys, manage channels or receive payments for the player. It connects to a wallet through Bitcoin Connect and uses wallet methods exposed through the provider. The user's wallet remains the payment authority.

Do not describe it as a mature tournament platform. The screen text invites screenshots and a tag for a chance to win, and the app reads a prize-pool balance. The visible source does not prove a complete contest administration system. That distinction is important for anyone tempted to play for expected return rather than curiosity.

The fair description is stronger anyway: Zappy Bird is a small, open-source, Lightning-powered arcade demo that shows how NWC can make repeated, permissioned micropayments feel native inside an interactive web app. That is enough to make it worth documenting.

Who Zappy Bird is for

Zappy Bird is for people who learn protocols better by touching them. If NWC sounds abstract, the game turns it into a wallet prompt, a budget, a one-sat invoice, a score increment and a revoke decision. You can understand more in five minutes of careful play than in a dozen vague explanations of programmable money.

It is also for developers building small payment experiences. Games, dashboards, content unlocks, bots and tools all face the same question: how do you let an app request money without teaching users to approve reckless wallet access? Zappy Bird is deliberately simple enough that those questions are visible.

It is not for people who want polished consumer gaming, guaranteed payouts or a serious leaderboard. It is not for users who cannot tolerate losing a few sats to a failed experiment. It is not for anyone who wants to connect their main wallet and forget about the permission.

The best way to approach it is playful but disciplined. Create a tiny NWC connection, read the permission request, play a round, inspect the payments, revoke the connection and keep the lesson. The bird is funny; the authorization model is the serious part.

Closeout

Zappy Bird earns its place in the Games shelf because it makes Lightning payments feel physical. A jump, a pipe, a tiny invoice and a score increase all live in the same loop. That is a different learning experience from reading a wallet API page.

The Nostr connection is narrow and important. This is NWC as wallet-control infrastructure, not Nostr as social posting. The app asks for payment methods, uses a WebLN-shaped provider, and turns NWC into a game mechanic. That is exactly the kind of edge case that helps readers understand what NWC can do.

The risk is also narrow and important. Repeated payment authorization is useful only when the cap is small, the methods are clear and revocation is easy. Zappy Bird is fun because it is tiny. Keep your wallet exposure tiny too.

Open the live game, read the source, connect only a capped wallet, play one round and then disconnect. If a project this small can make you think twice about wallet permissions, it has already done more educational work than many larger demos.

Sources worth opening

Start with the live game, README and `src/index.ts`. Then read Bitcoin Connect, NWC, Alby SDK and Lightning Address sources so the payment loop is clear before you approve a wallet connection.

Back to the Crays Nostr page
Apps route visual cue 1
Apps route visual cue 2
Apps route visual cue 3
Apps route visual cue 4
Apps route visual cue 5

How to use this page

Keep the product map close.

Search the wider app shelf when you want another client, tool, protocol reference or source trail beside this page.

AppsKeep the Apps shelf openApps pages stay availableProducts, tools, communities and source trails.Browse pages
Apps contribution visual cue 1
Apps contribution visual cue 2
Apps contribution visual cue 3
Apps contribution visual cue 4
Apps contribution visual cue 5

Bring something back

Ask, suggest, submit or nominate.

Ask a question, send a source, suggest a fix, submit a project or nominate a public Nostr account. The page stays stable; your contribution gets reviewed beside it.