Tuesday, 8 September 2026

With Great Functionality Comes Great Legal Responsibility

It's unclear to me how many of you were actually old enough to use the internet back in the late 90s, but if you were, you might remember this charming little search engine called Ask Jeeves. It had a friendly cartoon butler, Jeeves, ask you what you wanted, and delivered you links, with a witty quip or two. It wasn't extremely powerful, but boy did it have personality!

Good old Jeeves.

Good old Jeeves has been retired for the last couple decades, but Google saw fit to try something in the same vein.

Google's version

It used to be that when you searched for something, Google would act like a normal search engine and give you a series of links to what you searched for. At most, it might suggest that you had mistyped your search, or aggressively promote content that people had paid them money to promote.

Relatively harmless run-of-the-mill stuff, as it were.

Then they started giving us an AI Overview, where an LLM would summarize the results of the search without you needing to visit those sites personally and draw your own conclusions. Which the site owners, understandably, weren't too thrilled at due to the decreased user traffic.

Google summary
on a platter.

At some point, the LLM-generated summary would even appear on top of all other searches (sort of like Jeeves serving you information on his silver platter) giving users very little incentive to do anything other than read that convenient summary. And that's where the trouble began.

In Munich, a regional German court took Google to task over two cases where the AI overview had gone wrong, with disastrous results. Apparently some publishing companies had been erroneously tied to scams and shady business practices due to a hallucination in the LLM. Not something completely unheard of, to be fair.

Google argued that they had explicitly stated that AI overviews could be wrong, they had included the original links, and users were able to click on those links for verification of the AI overview. The court tore that apart with the assertion that very few people ever did (kind of like reading a EULA and then clicking the "I have read the Terms and conditions" checkbox at the end) and that AI Overviews constituted Google's own content which it was legally liable for.

I'll concede that Google's AI Overviews are largely directionally correct, and the odd hallucinations are the exceptions that prove the rule. Unfortunately, due to Google's massive scale and reach, even a 0.1% chance of something going wrong represents a really bad day at the office for an unacceptable number of people.

A case I was reminded of...

Back in the day, Social Media platforms like Facebook and TikTok were being raked over the coals legally for misinformation of users. It's not the exact same case, but there are interesting similarities.

These platforms argued that they were mere content-displaying platforms and could not be held to the same standards as publications. However, the unfortunate fact was that they were implementing algorithms that largely dictated what users saw first. As long as they were doing this, the courts argued, they were effectively publications in their own right.

TikTok as a publication?

That does make sense. After all, don't publications also put their most compelling stories on the front page?

How are these cases similar, though? Well, in the original form of the services, all these companies did was, as claimed, display content provided by other parties. It was the value-added service by the companies themselves, which put them at legal risk. Also worth noting is, in both these cases, the offending mechanism were the default states. Meaning, they weren't configurations that the users could opt into. Therefore, the argument can't be made that "the users chose this", because they quite literally didn't.

Conclusion

User consent factors quite heavily into these lawsuits. It's often not enough to serve users something and then belatedly seek permission to do so. I could be wrong, but I suspect that none of this legal action would have happened if Google had gone with the classic display and only served the AI-enhanced version upon request.

Talk about courting trouble!
T___T

Saturday, 5 September 2026

Great Expectations and how to manage them, redux

Almost exactly a year ago, I spoke about great expectations, and managing them. The examples I provided were in a professional context. Often, however, the most relatable examples aren't professional, but personal.

It was 2024. I had just moved house, and that was another big item off my list. Actually, that was the final big item off my list. I went back to the WhatsApp groups that I had muted over the past year so I could concentrate on doing my thing, and found that in one of those groups, only a handful of them were left. Which was weird because I distinctly recalled them being close friends. What had happened in all the time I'd been minding my own business?

Spilling the tea.

Now that I had (relatively) more time on my hands, I made it a point to find out. Reaching out to those who were no longer part of the group, we met up for food and coffee, and with each of them, spoke at length about all the drama that had transpired during my absence.

The deets, sort of

Before I begin, let's get one thing out of the way. Aside from certain facts, everyone's view of the entire thing diverged in some wild way or other. And I have no reason to suspect that any of them was outright lying to me - after all, it's not like my opinion of them affected anything at all. Like I always say, no one is the villain in their own story. Not even the villains. Very few people have the honesty or humility to think of themselves as the bad guy.

Anyway...

One friend had put another friend up at his place, rent-free, when she was in need a few years ago. He complained to me that she was a nightmare - leaving the airconditioning on and racking up huge bills, not doing the housework, and so on. And I'm not saying that his complaints were illegitimate - if someone's living at your place for free, they should be considerate about racking up huge electricity bills and cleaning up after themselves.

At the same time, though, what the hell was this guy thinking? Why put himself in this position in the first place?

Look, being friends is one thing. But we all need to be conscious of the flaws of others (and ourselves, while we're at it) even if - especially if - these people are your friends. And consider the distance between you and those people carefully.

...your friends are my friends,
and my friends are
your friends...

Distance is healthy.

I'll be the first to admit that I can be an asshole with severe personality flaws. At the same time, I'm not putting anyone in a position where they have to put up with those flaws daily. (Except Mrs TeochewThunder... sucks to be her, I guess.) I recognize that it is far easier for people to stay friends with me if there's a healthy distance between us. My friends constantly annoy me by giving me unsolicited (and worse, garbage unqualified) advice, self-righteously pontificating about ridiculous causes I couldn't give less of a shit about, projecting their insecurities onto me... and I'm sure I annoy them in equal measure in a myriad of ways.

However, with distance, and freedom from expectations, we're all afforded the space to be ourselves without getting in anyone's way.

The lesser the distance is, the greater expectations become. In a way, this makes perfect sense. You wouldn't for example, feel betrayed by someone who wasn't a friend because you would have zero expectations of that person. You would never accuse a total stranger of "selling you out", because that is a deeply personal thing, and personal is the last thing you would associate with a stranger. If you were in a pinch, you would turn to friends or relatives for help, especially those who'd helped you before. It's human nature.

I never put my friends in a position where they can let me down. In that sense, I expect them to let me down given half a chance, so I don't give them the opportunity.

What all this means

Expectations were unrealistic.

Remember I said before, employers sometimes have unrealistic expectations? That they hire you to do one thing, but out of the blue they suddenly decide that they need you for some unrelated task? And then have the sheer audacity to be outraged when you don't magically become good at that unrelated task just because they expect you to be?

For example, if you were hired to be a software developer but your employer needed a lightbulb changed and decided you were the person for the job. Because there's probably this line in your employment contract that says your duties aren't limited to those stated, and that your employer can decide if there are new duties they can add. Or some bullshit like that.

And then when you ended up breaking the lightbulb, they acted like you just killed their entire family.

Oops!

Well, that's because there are their expectations, and then there is reality. And sometimes they just don't meet. If you hire someone to develop software and but ask them to screw in lightbulbs instead, and they end up breaking the lightbulb, whose fault is it, really? If a screwdriver got damaged because you tried to use it like a hammer, whose fault is it? Yours. Don't look elsewhere. You're the idiot in this story.

My buddy had expectations of our mutual friend, but she just wasn't the person he wanted her to be. As reasonable as he thought those expectations were, perhaps the reality is that she's a dog that shits everywhere it pleases.

You meet people where they are, and stop trying to change them. 

Permission to be Imperfect

Some say my standards are too low. I disagree. In fact, my standards, both for myself and others, are ridiculously high. But my expectations aren't. The problem isn't standards. The problem is that I think I'm better than everyone else. None of you reading this will ever be half as awesome as I think myself to be.

See, I have standards that I strive to live up to. But I often fail because, as mentioned before, my standards are high to an absurd degree. Recognizing that I'm only human, I often forgive myself for that failure and try to do better. I expect to fail because I'm not perfect and I've made my peace with that. But if I'm not perfect, and I still consider myself better than the rest of you, it would be monstrously unfair of me to expect better from everyone else. If I'm not perfect, nobody has any business being perfect.

A flawless diamond.

Therefore, you are all excused for being imperfect. His Teochewness has decreed it.

The only time one can expect perfection is when writing a program. And even then, unless it's overly simple, it can only be perfect to a point. The more moving parts a programmer introduces into any given system, the greater the likelihood that the system will break in an extreme use case.

Guess what else is full of moving parts? Human beings.

People expect software to be perfect given that it has no feelings and that it's basically ones and zeroes. What they forget is that software is made by imperfect human beings. Even if, in the future, software is created by Artificial Intelligence, don't forget where AI learned all that from - imperfect human beings.

Oh wow, look at that. I managed to insert a rant about AI in here. Way to go, old man.

The Expectant Conclusion

Know when to stop. That's a lesson that can be applied to almost everything in life. Where friends are concerned, it's not a matter of "the closer the better". Understand that there is such a thing as too close, because people suck.

In relation to the above, understand when expectations are being unreasonably raised. Every time you declare that someone's your bro and you'll take a bullet for him, you raise expectations that you'll do so when the occasion arises. Every time someone asks for a favor and you oblige, you raise expectations that you'll help again when needed. You have to think about whether or not you can afford to raise those expectations. Remember previously I told you that how people treat you is a matter of how you train them to treat you? Well, start training others... and yourself. Establish that distance. Enforce those boundaries.

And yes... temper those expectations.

Permission to suck, granted!
T___T

Tuesday, 1 September 2026

A Baffling Encounter with a Tech Poseur

Social Media has never held all that much attraction for me. At some point, the novelty of interacting with people online turns into the drudgery of interacting with people, period. It was mid 2022 when this random encounter happened on the Clubhouse app, with some dude from Canada.

I introduced myself as a software developer, which seemed to signal to this fella that I needed to be challenged. He asked what I knew about Kubernetes, and when I honestly answered "not a lot", he then openly questioned how I could call myself a software developer and followed up by asking what I knew about SQL.


Again, seeing no reason to even attempt to impress a total stranger online, I said I wouldn't call myself an expert and just used as much as I needed to. This was totally subjective, of course. I know how to perform CRUD operations and database joins, and normalize database structures even if I've long forgotten the formal definition of BCNF. To some people, that would absolutely count as expertise, though I feel obligated to hold myself to higher standards.

In response, he started questioning my dedication to my craft, and this made me suspect that I wasn't speaking to someone who actually worked in tech... because genuine tech workers, even if they lacked humility, would probably have the experience to avoid simplistic (and frankly not terribly clever) snap judgments like these.

Why that was completely stupid

A couple reasons. First of all, Kubernetes isn't what I would call a mainstream tech thing. It's more of a DevOps thing, which still makes it a software tech discipline, just not first on everyone's to-do list.

A DevOps engineer would totally learn it.

A back-end engineer? Almost definitely.

A full-stack developer? Maybe.

A Data Analyst, Front-end engineer or Network Manager? Doubtful.

So acting like a techie is somehow deficient for not knowing much about it, tells me more about the person making that statement, than the one being judged. This would be as absurd as me mocking back-end engineers for not knowing HTML and CSS. Because, really, why would they? What next? You aren't a real musician if you can't play the zither? You're not a real doctor if you can't extract a tooth?

Real doctors can pull teeth!

Now SQL would be a more mainstream tool than Kubernetes. Regardless of which layer of the stack a tech worker is operating on currently, they would almost be guaranteed to have been taught SQL in school. And even then, plenty of tech workers don't deal with SQL professionally. Even I only really use raw SQL when I'm performing joins in database consoles - for actual software development, it's too much of a security risk and the accepted tools abstract away SQL for library functions.

Also, can I just mention that using Kubernetes and SQL together as a flex, is just kind of odd? Not pineapple-on-pizza weird. Maybe spinach-on-pizza weird.

Genuine Questions...

My industry isn't a circus, so why are we attracting these clowns? We were known as nerds in school. Why are people now so anxious to be be associated with us?

Why these clowns?

Oh, well. It's now 2026. With all the tech layoffs going on, perhaps my industry would have lost enough mystique to no longer produce these groupies.

Also, why does every tech poseur I encounter, feel the need to namedrop Kubernetes? Does it even sound that impressive?

I'm dead serious. Every other guy who tried to impress me with his tech cred, has cited Kubernetes. One even took the pains to explain Kubernetes was a upscaler in cloud infrastructure, as if we couldn't just read that up on its Wikipedia page. I'm not even that offended by the posturing. It's more the low-effort posturing, like I was some noob who can't tell when someone's full of hot air.

There are three situations where Kubernetes might be brought up casually as a subject.

One; DevOps engineers dissecting a scaling problem. Two; backend engineers deep in infra discussion. Three; tech performer using tech buzzwords as a big stick to hammer home the illusion of being technically experienced.

As far as I could tell, this did not fall under the first two categories. The irony is, in his eagerness to perform tech experience, he ended up demonstrating something that few actual software engineers would exhibit - ignorance of tech stacks.

So... what is it about Kubernetes specifically? This stumps me.

Finally

Of course, I don't mind being told I'm not much of a tech. I already don't consider myself exceptional. Although, the judgement might have stung more coming from someone who obviously knew his shit. As it was, it left me somewhat bemused. As a generalist, I didn't feel personally disrespected. But someone who actually spent the sweat and sleepless nights mastering Kubernetes and working with it, might feel quite differently.

It's been four years. Why am I even talking about this now? In truth, I wanted to talk about it way back, but more interesting stuff kept coming up. Plus, I am one hell of a procrastinator. It's a slow news week, so there you go.

How's your Java and JavaScript?
T___T

Tuesday, 25 August 2026

Web Tutorial: Pill Puzzle (Part 4/4)

We've developed the ability to place pills on either side of the scale, and return them to their original positions if required. Time to work on using the scale. For that, we ensure that the weigh() method is invoked when the button is clicked.
<div><button id="btnWeigh" onclick="pillgame.weigh()">WEIGH</button></div>


Then we work on this method. We begin with that return statement at the end. Then we exit early if playerStatus is not "ill".
weigh: function()
{
  if (this.playerStatus != "ill") return;

  return;

},


We also run setWeightTimes(), as we should.
weigh: function()
{
  if (this.playerStatus != "ill") return;
  this.setWeightTimes(this.weighTimes + 1);

  return;
},


Next, we define leftWeight. What we do is use the map() method to iterate through all the values of pills_left, which are indexes into the pills array, in effect making a new array based on all the weights of the pills that are in pills_left.
weigh: function()
{
  if (this.playerStatus != "ill") return;
  this.setWeightTimes(this.weighTimes + 1);

  const leftWeight = this.pills_left
  .map((x) => this.pills[x])


  return;
},


Then we use the reduce() method, totalling all the values.
weigh: function()
{
  if (this.playerStatus != "ill") return;
  this.setWeightTimes(this.weighTimes + 1);

  const leftWeight = this.pills_left
  .map((x) => this.pills[x])
  .reduce((sum, weight) => sum + weight, 0);

  return;
},


We do something similar for rightWeight. And after that, we have three If blocks to handle the various outcomes.
weigh: function()
{
  if (this.playerStatus != "ill") return;

  this.setWeightTimes(this.weighTimes + 1);
  const leftWeight = this.pills_left
  .map((x) => this.pills[x])
  .reduce((sum, weight) => sum + weight, 0);

  const rightWeight = this.pills_right
  .map((x) => this.pills[x])
  .reduce((sum, weight) => sum + weight, 0);

  if (leftWeight == rightWeight)
  {
    return;
  }

  if (leftWeight > rightWeight)
  {
    return;
  }

  if (leftWeight < rightWeight)
  {
    return;
  }


  return;
},


If leftWeight is heavier than rightWeight, pnlWeighPillsContainerLeft should go down and pnlWeighPillsContainerRight should go up, thus the justify-content properties of both need to be adjusted accordingly - pnlWeighPillsContainerLeft to flex-end (to sink to the "bottom") and pnlWeighPillsContainerRight to flex-start (to float to the "top"). The opposite is true for if rightWeight is heavier than leftWeight. And if both are equally heavy, both pnlWeighPillsContainerLeft and pnlWeighPillsContainerRight sink to the bottom.
weigh: function()
{
  if (this.playerStatus != "ill") return;
  this.setWeightTimes(this.weighTimes + 1);

  const leftWeight = this.pills_left
  .map((x) => this.pills[x])
  .reduce((sum, weight) => sum + weight, 0);

  const rightWeight = this.pills_right
  .map((x) => this.pills[x])
  .reduce((sum, weight) => sum + weight, 0);

  if (leftWeight == rightWeight)
  {
    $("#pnlWeighPillsContainerLeft").css("justifyContent", "flex-end");
    $("#pnlWeighPillsContainerRight").css("justifyContent", "flex-end");


    return;
  }

  if (leftWeight > rightWeight)
  {
    $("#pnlWeighPillsContainerLeft").css("justifyContent", "flex-end");
    $("#pnlWeighPillsContainerRight").css("justifyContent", "flex-start");


    return;
  }

  if (leftWeight < rightWeight)
  {
    $("#pnlWeighPillsContainerLeft").css("justifyContent", "flex-start");
    $("#pnlWeighPillsContainerRight").css("justifyContent", "flex-end");


    return;
  }

  return;
},


Let's try this! Place pill 1 on the left and pill 2 on the right.

Then click WEIGH button. See that it now indicates that the scale has been used once! In all likelihood, both pills are equal in weight, so both scale bowls should sink.

Refresh. Now place pill 1 on the left and pill 2 on the right... and then add pills 3 and 4 on the left.

Now weigh. The left side will sink and the right side will rise! And now the display says you have used the scale once.

Now place the rest of the pills on the right side...

...and weigh. The right side should sink and the left should float. The display should say you have used the scale twice, and the button should disappear.

Now for the final part!

Feeding any given pill to the mouth. For this, add a click action to the face. It should call placePill(), with "mouth" passed in as an argument.
<div id="playerFace" onclick="pillgame.placePill('mouth')"></div>


There are only two scenarios here - success or failure. That depends on the weight of the pill selected. We derive that by using the value of pill_selected as an index into pills. And this leads into the two If blocks.
if (slot == "mouth")
{
  if (this.pills[this.pill_selected] == 1)
  if (this.pills[this.pill_selected] == 10)


  return;
}


If the pill is light, we use setPlayerState() to set the player state to "well". If it's a heavy pill, then it's poison and you just killed the patient. Either way, we run gameOver() because the game is effectively over at this point.
if (slot == "mouth")
{
  if (this.pills[this.pill_selected] == 1) this.setPlayerState("well");
  if (this.pills[this.pill_selected] == 10) this.setPlayerState("dead");
  this.gameOver();


  return;
}


Now for the final method, gameOver(). We basically return stuff to factory settings, visually.
gameOver: function()
{
  $("#btnWeigh").hide();
  $(".pnlWeighPillsContainer").css("justifyContent", "center");
  this.pills_middle = [];
  this.pills_left = [];
  this.pills_right = [];
  this.renderPills();

  return;

}


Now remove the red lines and we're all set.
div {outline: 0px solid red;}


Final test!

It's easy to fail. Select any pill at random and click on the face. There's an 87.5% chance that you kill the patient.

But to cure the patient is the crux of how this puzzle is solved. Place pills 1, 2 and 3 on the left. Pills 4, 5 and 6 go on the right.

Weigh. You see that Pills 1, 2 and 3 are lighter, so the cure is definitely in that group.

Return pills 4, 5 and 6.

Return pills 2 and 3.

Place pill 2 on the right side of the scale, and weigh. Looks like pill 2 is still heavier.

Which makes pill 1 the cure. Feed it to the face, and you win!


We're done! Not too shabby, eh?

Fare "well",
T___T

Saturday, 22 August 2026

Web Tutorial: Pill Puzzle (Part 3/4)

All right! Now that we have a mechanism by which to display pills, we need to enable placing and returning them via the interface. For this, let us add a click action to the DOM. It will run placePill(), with an argument describing which location to place the pill in.

<div id="pnlWeighPillsContainerLeft" class="pnlWeighPillsContainer">
  <div class="pnlWeighPills" id="pnlWeighPills_left" onclick="pillgame.placePill('left')">
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div><div class="bowl"></div></div>
  </div>        
</div>

<div class="scaleContainer">
  <div class="pnlScale">⚖</div>
  <div>Times Used: <span id="pnlTimesUsed"></span></div>
  <div><button id="btnWeigh" onclick="pillgame.weigh()">WEIGH</button></div>
</div>

<div id="pnlWeighPillsContainerRight" class="pnlWeighPillsContainer">
  <div class="pnlWeighPills" id="pnlWeighPills_right" onclick="pillgame.placePill('right')">
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div class="pillSlot"></div>
    <div><div class="bowl"></div></div>
  </div>        
</div>


We'll work on the placePill() method next. It accepts a parameter, slot. We have the default return statement, and a guard clause that says if playerStatus is anything but "ill", nothing happens. Which makes sense; in those cases the game is over and nothing needs to happen.
placePill: function(slot)
{
  if (this.playerStatus != "ill") return;

  return;

},


Now let's have a few cases. The first case is for if no pill is selected; that's when we return the pill rather than placing it. The second case is for if slot is "mouth", which means a pill has been selected and we're about to feed it to the face at the bottom. The last two are "left" and "right", and those don't have curly brackets because they're one-line operations.
placePill: function(slot)
{
  if (this.playerStatus != "ill") return;

  if (this.pill_selected == -1)
  {
    return;
  }

  if (slot == "mouth")
  {
    return;
  }

  if (slot == "left")
  if (slot == "right")


  return;
},


Let's handle the last two cases first. If pill_selected is not -1, that means there is a pill selected. If that's the case, we run the unshift() method on the appropriate array to place the selected pill at the beginning of the array.
placePill: function(slot)
{
  if (this.playerStatus != "ill") return;

  if (this.pill_selected == -1)
  {
    return;
  }

  if (slot == "mouth")
  {
    return;
  }

  if (slot == "left") this.pills_left.unshift(this.pill_selected);
  if (slot == "right") this.pills_right.unshift(this.pill_selected);

  return;
},


After that, declare index, and check if pills_middle still has the pill selected. If it does, remove it using the splice() method. Then set pill_selected to -1 and run renderPills().
placePill: function(slot)
{
  if (this.playerStatus != "ill") return;

  if (this.pill_selected == -1)
  {
    return;
  }

  if (slot == "mouth")
  {
    return;
  }

  if (slot == "left") this.pills_left.unshift(this.pill_selected);
  if (slot == "right") this.pills_right.unshift(this.pill_selected);

  const index = this.pills_middle.indexOf(this.pill_selected);

  if (index !== -1) this.pills_middle.splice(index, 1);

  this.pill_selected = -1;
  this.renderPills();


  return;
},


Let's test this! Click START. Click on pill 3 and then on the left side. Does pill 3 appear on the left?

Now click on pill 4 and then on the right side. Does pill 4 appear on the right?

What if you then clicked on pill 7 and then on the left side?

Go on, add more pills on the left and right side of the scale. Notice how the pills disappear from the middle when you place them.

Now let's handle replacing the pills. If no pills are selected (meaning pill_selected is -1), running placePill() should result in running returnPill(). But I'm getting ahead of myself; let's begin with the first If block in the placePill() method. We establish two more If blocks in those, and an early return statement after. These check for the value of slot, and ensure that there are actually elements in the relevant array before taking any action.
if (this.pill_selected == -1)
{
  if (slot == "left" && this.pills_left.length > 0)
  {

  }

  if (slot == "right" && this.pills_right.length > 0)
  {

  }

  return;

}


Here, you set pill_selected as the first element of pills_left. Then you remove it from pills_left using the shift() method.
if (this.pill_selected == -1)
{
  if (slot == "left" && this.pills_left.length > 0)
  {
    this.pill_selected = this.pills_left[0];
    this.pills_left.shift();

  }

  if (slot == "right" && this.pills_right.length > 0)
  {

  }

  return;
}


With that, you run the returnPill() method (we'll build that soon) and then renderPills() to update the display.
if (this.pill_selected == -1)
{
  if (slot == "left" && this.pills_left.length > 0)
  {
    this.pill_selected = this.pills_left[0];
    this.pills_left.shift();
    this.returnPill();
    this.renderPills();

  }

  if (slot == "right" && this.pills_right.length > 0)
  {

  }

  return;
}


We perform the analogous operations for the opposite side.
if (this.pill_selected == -1)
{
  if (slot == "left" && this.pills_left.length > 0)
  {
    this.pill_selected = this.pills_left[0];
    this.pills_left.shift();
    this.returnPill();
    this.renderPills();
  }

  if (slot == "right" && this.pills_right.length > 0)
  {
    this.pill_selected = this.pills_right[0];
    this.pills_right.shift();
    this.returnPill();
    this.renderPills();

  }

  return;
}


Now, what does returnPill() do?
returnPill()
{
  return;
},


It places the selected pill back in pills_middle via the push() method. Once that is done, pill_selected becomes -1 again.
returnPill()
{
  this.pills_middle.push(this.pill_selected);
  this.pill_selected = -1;


  return;
},


Go on, try it. Reproduce what we did in the previous screenshot, then click on the left side of the scale. Does pill 7 get sent back to the middle?

We've been using a fair amount of JavaScript array methods here. If you feel like reading up further, here are some references.
- shift()
- unshift()
- push()
- splice()

Next

Weighing the pills and delivering the cure.