← All posts

Phone to desktop via QR: Cinetomi's playback handoff

Jan 22, 20261 min readGoFlutter

On Cinetomi, users discover films on their phone but often want to finish watching on a bigger screen. The classic path is obvious: open the site on your computer, log in, find the film again. Three steps of friction. Our goal: tap "Watch on Desktop" on the phone, and the film continues on the computer from the exact second it left off.

How the flow works

In the browser, cinetomi.com shows a QR code and opens a session channel. When you scan it with "Watch on Desktop" in the app, the phone sends a one-time handoff token to that channel. The browser validates the token, receives the user's session and the film position, and starts playback.

The critical design decision: the QR contains no session data, only a short-lived channel id. Session information is never embedded in anything visible on screen.

Token generation on the Go side

Handoff tokens are single-use with a 60-second lifetime:

handoff.go
func CreateHandoff(userID, filmID string, position int) (string, error) {
    token := randomToken(32)
    err := rdb.Set(ctx, "handoff:"+token, HandoffPayload{
        UserID:   userID,
        FilmID:   filmID,
        Position: position,
    }, 60*time.Second).Err()
    return token, err
}
 
func ConsumeHandoff(token string) (*HandoffPayload, error) {
    // GetDel: read and delete; the token can't be used twice
    val, err := rdb.GetDel(ctx, "handoff:"+token).Result()
    ...
}

Thanks to GetDel, the token is deleted the moment it's read; someone photographing the QR can't replay it.

The only thing a user should feel during a device switch is continuity: the film resumes from the same second, the volume from the same level.

What we learned

The result is a success that's easy to measure: after the feature shipped, desktop viewing sessions started predominantly through the QR path.

Murat Özdemir

Product manager & mobile developer. Writes about product, performance and good interfaces.