You open your crypto app on your phone. A Bitcoin ticker loads. You glance at the number, put the phone down, pick it up a minute later, and the number is exactly the same. Either BTC hasn't moved a cent in 60 seconds, which is extremely unlikely, or your mobile ticker is lying.
Mobile browsers and native apps behave very differently from a desktop tab. Every ticker that feels fast on a laptop has subtle failure modes on a phone. This post walks through the three big ones, and the defensive patterns that fix them.
Failure 1: Tab Suspension
When you switch apps or lock your phone, both iOS Safari and Chrome on Android aggressively suspend background tabs. Timers stop firing. WebSocket connections are kept open at the TCP level in some cases, but message handlers don't run. From the JavaScript's point of view, time stops.
When you refocus the tab, the ticker's last rendered price is still whatever it was when you switched away. The element shows that number confidently. There is no loading state, no stale indicator, nothing to tell you the feed is behind. The browser resumes JS execution and the ticker starts updating again within a second, but for one long second, you're looking at a lie.
The fix is a visibilitychange listener that treats regained visibility as a trigger for a fresh fetch.
This pattern is non-negotiable on mobile. Without it, every ticker on every phone lies for a beat after refocus. With it, the user sees a fresh price almost instantly when they come back.
Failure 2: Radio Handoff
Your phone's network changes constantly. WiFi to cell, cell to WiFi, one tower to another, 5G to 4G when you step into an elevator. Each handoff breaks open TCP connections. The WebSocket was alive on WiFi; the handoff happens; the new connection is on LTE; the old socket is dead.
The browser doesn't always notice quickly. From the app's perspective, the WebSocket looks open but no messages arrive. Ten seconds, twenty seconds pass with no updates. The ticker freezes.
The fix is a heartbeat. Expect at least one message within N seconds. If the deadline passes with no activity, assume the connection is dead, close it, and reconnect.
15 seconds is a conservative threshold for a BTC stream during active hours. During low-activity periods (weekend pre-dawn hours), Binance may genuinely send no trades for 10+ seconds. Tune based on your feed.
Failure 3: Battery Throttling
iOS and Android actively throttle JavaScript execution on backgrounded or low-power tabs. A setInterval(fn, 1000) on the foreground tab fires every second. The same interval on a phone in low-power mode might fire every 5 seconds or pause entirely.
The OS does this to save battery. From a ticker's perspective, it means even if the tab is technically "active," your polling cadence is a suggestion, not a guarantee. And if you fight it, you lose: running a high-frequency timer is exactly the pattern battery-savers are designed to kill.
The fix is not to fight it. Throttle more aggressively on mobile. Accept slower updates. Pair that with a clear "last updated" timestamp so the user can see the actual cadence.
TerminalFeed's approach: desktop paints at 1 second, mobile at 3 seconds. WebSocket throttled at the render layer, not the socket layer. The socket still receives every trade event, the renderer just batches.
Failure 4: Silent Stream Failures
Mobile networks drop packets quietly. A WebSocket connection may silently lose a message without any retransmission at the application layer. Your client sees a gap in the price stream but has no way to know it's a gap.
Mitigation: always display a "last updated" timestamp prominently. If the user sees "2 minutes ago" next to the price, they know the number is stale before they act on it. If the UI just shows the price with no time context, they have no defense.
Combined with a CSS rule like .stale { color: #8A8880 }, the UI visually dims when the feed falls behind. The user sees the problem without needing to read the timestamp.
Failure 5: Animation and Layout Thrash
Every pixel you paint on mobile costs battery and frame budget. A ticker that pulses, animates, or re-renders adjacent elements on every tick turns into a performance problem when layered into a dashboard with 30 panels.
Rules we enforce on mobile:
- Use
content-visibility: autoon panels so offscreen elements don't render. - Disable the "flash" animation on price changes on mobile. Flash up/down color change, not a full transition.
- Skip shimmer skeletons. Mobile users see the loading state for milliseconds, shimmer just costs CPU.
- Prefer
textContentupdates over DOM manipulation. Replacing a text node is cheaper than replacing elements.
Failure 6: The Zombie Connection
When the user navigates away from the ticker page to another page inside your SPA, the WebSocket should close. If it doesn't, it keeps running in the background, using battery and bandwidth, never surfacing data anywhere. Users open five tabs, each spawns a BTC WebSocket, none close, the phone gets hot.
Always close sockets in cleanup:
For non-React apps, attach a beforeunload or pagehide handler that closes the socket. It's one of the easiest resource leaks to miss and one of the most frequent.
What TerminalFeed Does on Mobile
Our mobile BTC hero follows the same WebSocket-primary, REST-fallback pattern as desktop, with these adjustments:
- Paint throttle raised from 1s to 3s.
visibilitychangelistener force-refreshes the REST endpoint on tab refocus, then reconnects the socket.- Heartbeat at 15s. Missing heartbeat triggers socket close and reconnect.
- Live dot animation disabled. Color-change on up/down, no pulsing.
- Stale indicator dims the price after 30 seconds without an update.
- Socket closes on route change (dashboard to article, or hash navigation).
- All polling intervals globally doubled on mobile as a cheap default (3s desktop becomes 6s mobile for non-hero panels).
The Pattern, Condensed
Mobile tickers are a trust problem. The user cannot tell when a price is stale. They will make decisions based on what they see. Build accordingly:
- Always show a "last updated" timestamp or age indicator.
- Refresh immediately on tab refocus.
- Heartbeat-driven reconnect on dead sockets.
- Throttle paints to conserve battery.
- Visually dim stale values.
- Clean up on unmount, always.
A ticker that follows all six rules tells the truth. A ticker that skips any of them lies sometimes, and you will not know when.
For the desktop architecture, see Why We Built a WebSocket Bitcoin Ticker Instead of Polling. For the full code walkthrough, How to Add a Free Bitcoin Ticker to Your Website.
Test the Ticker on Your Phone
Open TerminalFeed on mobile, background the tab, come back. The ticker refreshes immediately and tells you how old the last update is.
Open on Mobile API Docs