Flappy bird

My Adventure Making Flappy Bird Interactive on TikTok Live

Nahid Guliyev
4 min readNov 10, 2023

--

Introduction: I set out on a mission to make Flappy Bird interactive on TikTok Live. The idea was simple: let a random viewer control the game by liking the stream. Little did I know, turning this vision into reality would lead me down a path filled with surprises and challenges.

Setting Up Flappy Fun in JavaScript:

To start, I needed a working Flappy Bird game in JavaScript. I chose a slightly faster version than writing everything myself, lovingly named “Floppy Bird.” Using window.requestAnimationFrame, It is made sure the bird and pipes moved smoothly on the screen. Setting up the bird and pipes was the first step in creating this interactive experience.

const setup = () => {
currentScore = 0;
flight = jump;

// set initial flyHeight (middle of screen - size of the bird)
flyHeight = (canvas.height / 2) - (size[1] / 2);

// setup first 3 pipes
pipes = Array(3).fill().map((a, i) => [canvas.width + (i * (pipeGap + pipeWidth)), pipeLoc()]);
}

and render part.

const render = () => {
// make the pipe and bird moving
index++;

// ctx.clearRect(0, 0, canvas.width, canvas.height);

// background first part
ctx.drawImage(img, 0, 0, canvas.width, canvas.height, -((index * (speed / 2)) % canvas.width) + canvas.width, 0, canvas.width, canvas.height);
// background second part
ctx.drawImage(img, 0, 0, canvas.width, canvas.height, -(index * (speed / 2)) % canvas.width, 0, canvas.width, canvas.height);

// pipe display
if (gamePlaying){
pipes.map(pipe => {
// pipe moving
pipe[0] -= speed;

// top pipe
ctx.drawImage(img, 432, 588 - pipe[1], pipeWidth, pipe[1], pipe[0], 0, pipeWidth, pipe[1]);
// bottom pipe
ctx.drawImage(img, 432 + pipeWidth, 108, pipeWidth, canvas.height - pipe[1] + pipeGap, pipe[0], pipe[1] + pipeGap, pipeWidth, canvas.height - pipe[1] + pipeGap);

// give 1 point & create new pipe
if(pipe[0] <= -pipeWidth){
currentScore++;
// check if it's the best score
bestScore = Math.max(bestScore, currentScore);

// remove & create new pipe
pipes = [...pipes.slice(1), [pipes[pipes.length-1][0] + pipeGap + pipeWidth, pipeLoc()]];
console.log(pipes);
}

// if hit the pipe, end
if ([
pipe[0] <= cTenth + size[0],
pipe[0] + pipeWidth >= cTenth,
pipe[1] > flyHeight || pipe[1] + pipeGap < flyHeight + size[1]
].every(elem => elem)) {
gamePlaying = false;
setup();
}
})
}
// draw bird
if (gamePlaying) {
ctx.drawImage(img, 432, Math.floor((index % 9) / 3) * size[1], ...size, cTenth, flyHeight, ...size);
flight += gravity;
flyHeight = Math.min(flyHeight + flight, canvas.height - size[1]);
} else {
ctx.drawImage(img, 432, Math.floor((index % 9) / 3) * size[1], ...size, ((canvas.width / 2) - size[0] / 2), flyHeight, ...size);
flyHeight = (canvas.height / 2) - (size[1] / 2);
// text accueil
ctx.fillText(`Best score : ${bestScore}`, 85, 245);
ctx.fillText('Click to play', 90, 535);
ctx.font = "bold 30px courier";
}

document.getElementById('bestScore').innerHTML = `Best : ${bestScore}`;
document.getElementById('currentScore').innerHTML = `Current : ${currentScore}`;

// tell the browser to perform anim
window.requestAnimationFrame(render);
}

you can find full implementation here.(PS.i’m not the owner.)

Connecting to TikTok Live Connector:

My plan was to use the TikTok Live Connector to turn likes into controls for Flappy Bird.

tiktokLiveConnection.on('like', data => {
console.log(`${data.uniqueId} sent ${data.likeCount} likes, total likes: ${data.totalLikeCount}`);
})

Disappointment:
Excitement turned into disappointment when I found out the library had a timeout issue in each socket requests, and fixing it wasn’t straightforward and even it would have that option. “Tiktok Live” doesn’t give me the each like and correct timing.Whenever user likes the stream, after 1s i got a response like this data structure.

{
likeCount: 3, // likes given by the user (taps on screen)
totalLikeCount: 21349, // likes that this stream has received in total (from all users)
userId: "",
secUid: "<>",
uniqueId: "",
nickname: "",
profilePictureUrl: "",
rollowRole: 0, // 0 = none; 1 = follower; 2 = friends,
userBadges: [
{
type: "",
name: ""
},
],
userDetails: {
createTime: "0",
bioDescription: "",
profilePictureUrls: [
"",
]
},
followInfo: {
followingCount: 617,
followerCount: 112,
followStatus: 1,
pushStatus: 0
},
isModerator: false,
isNewGifter: false,
isSubscriber: false,
topGifterRank: null,
msgId: "7137750883651619630",
displayType: "pm_mt_msg_viewer",
label: "{0:user} liked the LIVE"
}

let say i got likeCount 3 I don’t know exactly when user clicked “like“ and how should i handle network lags. Instead of diving into complicated solutions, I decided to explore other options.

Learning from Mistakes: Every experiment comes with lessons. While my initial idea didn’t go as planned, it taught me valuable things. Sometimes, failing at first opens the door to better and more innovative solutions. Admitting mistakes is part of the process.

Conclusion: The Journey Continues: Though I didn’t achieve instant success, my journey making Flappy Bird interactive on TikTok Live was a learning adventure. From setting up the game in JavaScript to overcoming challenges with the TikTok Live Connector and finding a different way to control the game, each step taught me something new.

As I keep tweaking and exploring, the adventure isn’t over. Maybe the next attempt will bring the interactive Flappy Bird experience to TikTok. Until then, the quest for engaging live streaming experiences continues.✌️

--

--