Thursday, 31 July 2025

Do techies lean Liberal or Conservative? (Part 2/2)

Welcome back!

As promised, we will be diving into the three points I outlined previously, and we'll examine how these positions could actually be Conservative ones rather than Liberal.

How Software Developers are Conservative

Identity does not matter. This principle applies both ways. The same way we wouldn't discriminate on basis of race or gender or what-have-you, we also would not use it as a criteria of preference. You may claim this is contradictory, and you'd be wrong. This is entirely consistent with our worldview. Only the tech matters.

This isn't important, or
even relevant.

Now, sure, I know Silicon Valley has DEI programs in place. I promise you, those are most assuredly implemented by HR, or Management who want to appease some Liberal demographic. The average techie, however, simply does not give a flying fuck.

You ask any techie, and their first question is more likely to be "what's your tech stack?" rather than "what are your pronouns?"

Innovation versus tradition. Now, earlier when I said that techies prefer innovation over tradition, that was true. It may also have given you the impression that techies are risk-taking swashbucklers, which, to be honest, makes us sound more awesome than we probably deserve.

The truth is, techies are boring and consistent to a fault. We like things to be neat and make sense. If we did things a certain way, in the past, we are likely to do so again unless there are good reasons against it. We obey coding styles and naming conventions and follow software patterns because doing so makes it easy to troubleshoot when things go wrong.

Things are done a certain
way, for good reason.

One of our greatest frustrations is when people do things willy-nilly and just "wing it" because this is messy and asking for trouble down the road. We don't do things one way because we suddenly feel like doing so - we do them a certain way because it's worked for us in the past, but we are open to changing it if there's good reason to do so. Again, this is consistent with the previous, seemingly Liberal, position.

The collective versus the individual. This next point is more true now than it was when I first started out.

Developers accomplish things in teams. Software development is a team sport. There is really no way around it in this day and age. Tech has evolved so much that there is zero chance of one person knowing everything - now this knowledge requires a team.

Functioning as a unit.

Remember I said that developers are a diverse lot? It is precisely this diversity that lends itself to building effective multi-disciplinary teams. And in this way, it is often the collective that trumps the individual.

Some parting thoughts on the Culture Wars

Even similar positions can take on a Liberal or Conservative lens depending on how one views it. This shows me that both sides actually have a lot in common, Culture Wars be damned. And this perspective today is perhaps more relevant than ever now that the Culture Wars have arrived at what I can only describe as a very odd place.

Americans on Social Media now squabble over the dumbest shit imaginable. Recently, I had the pleasure of watching Superman, and went on Facebook to check out some reviews and see how other moviegoers found the film. Imagine my surprise when I found people embroiled in intense arguments over whether Superman was an illegal immigrant. I suppose technically, he is, but don't these nerds have better things to talk about?

Apparently not! I soon came to the realization that this was bigger than I initially thought. It had stemmed from the director and producer, James Gunn, making a remark about how Superman was an immigrant. The Liberals began drawing parallels to recent treatment of immigrants in the USA. And then people were in each other's faces about how they didn't understand Superman and weren't real Superman fans if they preferred/didn't prefer this Superman.

I mean, attempting to gatekeep who is and isn't a fan of a fictional superhero character? Tell me you're a loser without telling me you're a loser.

I'd initially thought this argument was basically a bunch of loser nerds obsessing over obscure details. It turned out to be way more ridiculous than that - it was a mob of wankers on the internet all trying desperately to justify feeling morally superior to the other side. Conservatives such as Ben Shapiro started trashing Superman as "woke garbage" and bitching in bizarre fashion about how the character wasn't American enough. One Susan Sarandon went onto Instagram to make it all about Israel and Palestine.


True, being good and kind does not require athleticism or talent. Any idiot is capable of kindness, and I would have it no other way. On the other hand, it just feels like a whole bunch of untalented schmucks just decided to overcompensate for their lack of talent and real-world achievement by blowing their trumpets about "kindness and empathy" just so they could feel accomplished, or something.

And as for people like Shapiro and Sarandon... massive cringe, man. People need to understand when they've taken things far enough and it's getting absurd. It's come to a point where people can't sit down and watch a decent superhero movie without trying to score political points. 

Dysfunction, thy name is America.


Your cultured programmer,
T___T

Monday, 28 July 2025

Do techies lean Liberal or Conservative? (Part 1/2)

Since the day I became aware of the Culture Wars over in the good old US of A, there's been a nagging question in the back of my mind. What side do techies really stand on? I'd like to think that we aren't a slave to any ideology - especially coming from Singapore, we're a pragmatic people who believe in whatever works in the most efficient way possible.

Silicon Valley does appear largely Liberal, or at least, that's what they would have us think. Jeff Atwood of Coding Horror, founder of Stack Overflow, actually jumped onto the anti-Trump bandwagon. However, after the recent electoral victory by the Republicans on the 5th of November last year, Big Tech has quite handily changed their tune. As I've often suspected, that's not the tech part speaking; that's the business part. That the appearance of Liberal domination in Silicon Valley was a veneer, and ran no deeper than that.

Nothing deeper than that!

And thus the question was begged. The answer, of course, that software developers, like many people, identify with both. They have some positions that are Liberal, and some that are Conservative.

The funny thing is that I identified three positions that could be considered both Liberal or Conservative, depending on one's perspective.

How Software Developers are Liberal

Identity does not matter. Software development is a field where logic reigns supreme. At some levels, it does not even require a particularly high I.Q, just the ability to do basic math and thick logically. Gender, race and sexual orientation don't determine an individual's ability to do math, and as such, are irrelevant when it comes to the ability to code, test and deploy software. A software developer is not going to automatically think that another software developer is competent based on identity.

Software
development isn't
just mens' work.

In fact, one of the most baffling (and frankly insulting) things I have heard as a developer is being told that I should be better at the job because I'm a man. What, and my years of hard work count for nothing? I'm naturally supposed to be better at it because God made boys engineers by default? Well, that's not what this tech thinks, and any male tech who tells himself that kind of rubbish, does himself (or anyone) no favors.

Innovation versus tradition. Don't get me wrong, we do have our traditions. But technology itself is a result of innovation and experimentation; daring to try new things. Thus, you will never hear a software developer saying that we should do something this way just because that's how we've always done it. That kind of argument holds no water for us. It runs counter to everything that drives a techie.

Tradition has its
charm.

We may respect tradition, but we are not slaves to it. There are no sacred cows. Tradition has its place, but tradition should also know its place.

We have tech innovations today precisely because we dared to go against tradition. We dared to challenge the pre-existing ideas of what was possible, or acceptable. What separates us from the herd is the willingness to ruthlessly drop tried and tested methods in favor of demonstrably better methods.

The collective versus the individual. Skillset-wise, you will never see a more diverse group. That's because in the tech landscape where there are very frequent changes and new things to learn, people end up learning across different (but often related) tech disciplines or going deeper into existing ones.

Tech skillsets come in all
shapes, sizes and colors.

There are no unifying standards as to what makes a techie, though there's been no shortage of would-be gatekeepers trying to keep things neatly classified. It's a lost cause.

Take any two developers, even from a broad collection of, say, back-end developers, and you may end up with one mostly trained in Java and one trained in Python. Take your two developers from a less broad group of Python developers, and you might get one who's more of a web scraper and one who's more of a data analyst.

Yes, tech itself is a huge domain, and evolving as I write this. Consequently, its practitioners are similarly varied.

Next

How we're Conservative.

Wednesday, 23 July 2025

JavaScript now has negative indexing... sort of

Searching arrays in JavaScript (or any language, really) can be a tricky proposition. One of my favorite ways to search for a value within an array in JavaScript, is the indexOf() method.

With this method, I just need to invoke it while passing in the string to search for, as an argument.
var arr = ["This", "is", "a", "test", "array"];
var index = arr.indexOf("test"); // 3

But what if I wanted it the other way round? What if I wanted to find out what value was the, say, third element in an array? Well, simple enough.
var arr = ["This", "is", "a", "test", "array"];
var val = arr[2]; // "a"

Now, what if I wanted the second last element in the array?

In Ruby, one could do this. This is known as negative indexing.
arr = Array["This", "is", "a", "test", "array"]
val = arr[-2] // "test"

In JavaScript, this doesn't fly. Get the length of the array, less 2, then use it as the index.
var arr = ["This", "is", "a", "test", "array"];
var val = arr[arr.length - 2]; // "test"

There is a way to use negative indexing in JavaScript, though. Use the slice() method.
var arr = ["This", "is", "a", "test", "array"];
var val = arr[-2, -2]; // ["test"]

The end result is an array, though. So there's one more step to take before you can access the value.
var arr = ["This", "is", "a", "test", "array"];
var val = arr[-2, -2]; // ["test"]
val = val[0]; // "test"


So, those are the things you have to do, in order to get the value of an element n times from the end... or do you? Now with the at() method, you literally just need to do this.
var arr = ["This", "is", "a", "test", "array"];
var val = arr.at(-2); // "test"

What is this sorcery?!

The at() method was introduced as recently as 2023. With it, a programmer can do in JavaScript what one can do in Python and Ruby - use negative indexes. Is that really so important considering that functionally, we already had all we needed in the slice() method?

slice(), slice(), baby.

Well, the slice() method complicates things. Sure, it's a handy method that can be used for big or small cases, but for the case we're talking about, it's complete overkill. Using the at() method makes code a lot more readable, and the importance of that cannot be understated.

Conclusion

It's always fun to see how programming languages evolve. Would have been nice for me to discover the at() method sooner than years after it emerged, but this only goes to underscore the fact that things change so fast in this industry and there's often something new to learn.

["Thanks", "for", "reading", "and", "see", "you", "again!"],
T___T

Saturday, 19 July 2025

Five Reasons to learn Web Development in 2025

Recent events such as the rise of generative A.I, have made tech work a little less attractive than it used to be. Web development, in particular, has suffered. That's probaby because a large chunk of web development is automatable, and even before A.I came on the scene, there had been numerous tools such as Content Management Systems and Low-code development platforms.

Thus, web development being automated by A.I was par for the course.

Robots writing websites.

Still, not all is lost. While web development might have lost much of its luster, there are still good, strong reasons to pick it up in ones tech career. Unlike the tech reporters and HR executives who write listicles like these, I have actually been a web developer before. I speak from experience, my dudes. And following are some of the most compelling reasons I had, in no particular order of importance, for going down this path.

1. No complicated installation

Ever tried to learn a language like PHP or Java? Every single one of these languages requires you to set up some kind of compiler or interpreter environment. PHP requires an Apache server. Java needs the Java Runtime Environment. You can write all the code you want, but until the code gets compiled or interpreted by the environment that you have to install and set up, you're not getting even a Hello World program done.

All you need is a browser.

HTML, CSS and JavaScript, however, do not. All of them already run in any major browser - Firefox, Chrome, and so on. In effect, the environment is right there for you.

This is not to say that you will never need to do any complicated installation. But for the basic building blocks - again, HTML, CSS and JavaScript - of web development, you don't. You will need to do that when you want to pick up a server-side language and maybe databases and definitely for the NodeJS style of development. But for basic stuff? Even the slightly more advanced stuff? Nope, not even a little bit. That is a lot more than you could ever say about other programming languages or platforms.

2. Good skill spread

When you learn web development, you learn HTML, CSS and JavaScript as a base starting point. That's already a good spread right there.

HTML and CSS are where you learn front-end and possibly even design. When you learn JavaScript, in addition to all the things you pick up when learning a programming language such as operators, arrays, branching and iterative logic, you also learn asynchronous operations and DOM manipulation.

A good spread of tools.

That's not to say that other tech disciplines don't have their own unique perks. But where it comes to the skill spread, web development wins. I don't think anything else even comes close.

Once you get past the basic toolset of HTML, CSS and JavaScript, back-end programming and databases will come into play. It's never just web development. Even if you are averse to the thought of being a humble web developer for the rest of your career, there are far worse places to start.

3. Resources

Now, when I say "resources", I don't just mean documentation, references and learning materials, though there's plenty of that, yes. But web development is not special in that regard because any other tech discipline boasts plenty of learning resources and a community dedicated to helping each other learn.

A good learning
community.

Though, in this case, web development has something extra.

You see, every humble HTML page on the internet can have its source viewed and played with in the browser, reverse engineered, and so on. Every URL on the internet is potentially a resource for learning, much like how I learned to cobble together JavaScript widgets decades ago.

In contrast, it's not possible to just take any desktop application and reverse-engineer the code, because the code has already been compiled and is no longer human-readable.

4. Ready use case

Often, when learning a programming language, it's helpful to be able to use newly-acquired skills to build something, so as to really hammer home the muscle memory. Something both relevant and useful, preferably. Not that Hello World programs don't have their place, but if one wishes to level up, better use cases are the order of the day.

And with web development, those use cases are almost too easy to find. Web development creates web pages, at the minimum. And after that, at varying levels of complexity, web applications. One does not have to stretch too far to find something worth building... and because it already exists, you know that it is both worth building and possible to build.

Applying what you learn.

My larger point is that what you learn can readily be applied. Not just in creating and editing websites, but in general software development. This also means that your chances of landing a job with that skillset cannot be understated. In this day and age, web developers are perhaps not nearly as in demand as they were a decade ago, or paid nearly as well, but the skillset goes beyond just web development.

For example, a lot of existing software already leverage things like REST API endpoints. These are basically URLs, which are pretty much the backbone of the web. REST is an almost inescapable part of the whole web development deal. Ergo, if you deal in web development, at some point you are going to be dealing with REST endpoints, which overlaps a large part of software development regardless of discipline.

Or even mobile development. In case you weren't aware, a large chunk of mobile tech is basically HTML, CSS and JavaScript.

I could go on... but do I really need to?

5. No gatekeeping

In the legal profession, there's the Bar Exam. In the medical profession, there's the Medical Regulatory Authority. In tech? Other than job interviews which exist at almost every industry, there's almost no gatekeeping in tech. Even the requirement for Degrees of Diplomas is not a really hard one.

When I say "no gatekeeping", I don't mean that nobody tries to gatekeep. The fact is that many people try to gatekeep, but it just doesn't work because to gatekeep, one needs a unified set of standards. It's almost impossible to establish said standards in a landscape as varied as tech, whose goalposts shift constantly.

The gatekeeper.

And while this inability to gatekeep exists in many areas of tech, none moreso than web development. HTML, CSS and JavaScript are fairly stable at this point, but these are just the base technologies. Their offshoots - frameworks, libraries and the like - keep springing up like mushrooms. And when you consider databases and backend programming languages, the possibilities multiply even more.

All in all, one could come in anytime in web development, and still be relatively fresh and relevant. No one can stop you from making and publishing web pages and applications, not in the same way they can stop you from practising law. You don't need a license to write code, so nobody can revoke it.

Some clarifications

The reasons stated here are in relation to those for choosing other tech fields. Why, for instance, web development when you could go for Data Analytics or cybersecurity? Reasons specific to web development.

I was inspired to compile this list because there are a lot of vague, generic and - to be brutally honest - trite lists out there on the web that extol the virtues of web development. Hopefully this is a better list.

<html>Bye for now,</html>
T___T

Saturday, 12 July 2025

Web Tutorial: The Cigarette Break Browser-based Screensaver

Following the one-year anniversary of having quit smoking, now that I no longer take smoke breaks, I do the next best thing - I make my browser take smoke breaks. Heh heh. For real, though. This occasion's web tutorial is centered around the idea of browser idle time. When the browser is left alone for a suitable amount of time, a screensaver comes up! And in this case, I want it to feature a smoking cigarette.

And the more time passes, the shorter I want the cigarette to get. Oh, and there needs to be animation.

Can I squeeze all of that into a single-part web tutorial? Let's find out!

Some HTML as usual. There's some Lorem Ipsum text, and a div with the id overlay. In the CSS, we want to set the outline for all divs to red.
<!DOCTYPE html>
  <html>
  <head>
    <title>Screensaver</title>
    <style>
      div { outline: 1px solid red; }
    </style>
    <script>
    </script>
  </head>

  <body>
    <h1>Some sample text. Leave this screen alone for 5 seconds to see the popup!</h1>    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque et justo sit amet libero interdum bibendum at nec dui. Mauris lacinia sapien nec blandit dapibus. Nunc suscipit in nunc in finibus. Praesent mi arcu, convallis auctor auctor luctus, gravida eget enim. Nunc quis finibus nisl, semper hendrerit nisl. Curabitur faucibus, velit varius efficitur suscipit, nibh ipsum bibendum turpis, placerat elementum nulla lorem eget diam. Phasellus lobortis aliquet leo, non aliquet mauris volutpat vel. Aliquam facilisis dui id sapien dignissim vulputate. Nullam cursus convallis sem vel maximus. Sed ac sollicitudin justo, at vehicula ligula. Praesent sit amet tellus massa. Nam nec varius ipsum.</p>
    <p>Cras varius, nisl vitae lobortis sodales, purus ligula dictum eros, volutpat finibus neque sem et ligula. Vivamus id odio varius, blandit elit sit amet, scelerisque enim. Sed luctus molestie leo, suscipit ultricies nisl elementum id. Donec lacus erat, laoreet vel viverra vel, aliquam vitae elit. Vestibulum venenatis congue lacus a facilisis. Nulla condimentum, metus volutpat rhoncus maximus, purus mauris imperdiet dolor, at bibendum lacus justo nec risus. Praesent scelerisque libero magna, at lobortis justo convallis vel. Suspendisse cursus, odio ultricies auctor laoreet, ligula leo vulputate diam, et efficitur mauris nisl et urna. Quisque sit amet rutrum magna. Aenean at vestibulum urna. Nam ornare justo a tortor molestie auctor. Quisque at malesuada mi, volutpat feugiat sem.</p>
    <p>Vestibulum luctus tempor ligula. Nulla id tortor ut est rutrum viverra. Etiam nec sapien id massa egestas dapibus at ut ipsum. Nunc sed tortor euismod, aliquet arcu et, commodo justo. Proin pretium vel neque sed maximus. In vitae vestibulum quam. Integer quis ex in ligula varius tempus. Donec diam arcu, faucibus eu aliquet nec, dapibus at sem. Sed convallis urna neque, tristique condimentum mauris condimentum vel. Praesent eu interdum quam, at ullamcorper augue. Mauris euismod odio libero, vel pharetra lorem egestas et. Nam laoreet ultricies venenatis. Integer ante risus, commodo nec eros ac, convallis tempus magna. Maecenas dictum lacus magna.</p>
    <p>Proin eu iaculis felis, sed lobortis lectus. Pellentesque malesuada diam eu porttitor aliquam. Fusce posuere dapibus odio vitae suscipit. Mauris consectetur, tortor et pulvinar auctor, ante eros ornare nunc, non fringilla neque quam sed erat. In velit turpis, ultricies ut egestas in, porttitor eget erat. Sed et risus molestie, maximus mauris eu, accumsan sem. Sed pellentesque feugiat elit. Integer nisl nulla, condimentum eu purus id, vestibulum commodo ligula. Phasellus consectetur, justo in mollis egestas, nisl felis laoreet ipsum, ac feugiat nunc ante sit amet risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla in enim lacus. Proin nulla dui, dictum at porttitor et, consectetur ac augue. Aliquam fringilla ex nec diam efficitur, vel vehicula mi molestie. Phasellus pellentesque in sem vitae porta. Nulla ut ultricies enim. Duis eu mauris sit amet lectus mattis efficitur molestie non magna.</p>

    <div id="overlay">
    </div>  </body>
</html>


Now, the modal popup is going to be visible by default - for now. For that we have the styling for overlay. position will be absolute, and we will set height and width properties to cover the entire screen with a translucent black background. left and top properties will be at 0px so as to begin on the top left corner of the screen. And of course, display is set to block to be visible by default.
<style>
  div { outline: 0px solid red; }

  #overlay
  {
    width: 100%;
 
    height: 100%;
 
    position: absolute;
 
    left: 0px;
    top: 0px;
    background-color: rgba(0, 0, 0, 0.5);
    display: block; 
 }

</style>


The first step of many steps!


Let's follow up by adding a div, styled with the CSS class content, in there.
<div id="overlay">
  <div class="content">

  </div>

</div>


We'll style it like this - a square with a black background and rounded corners set in the middle of overlay.
<style>
  div { outline: 0px solid red; }

  #overlay
  {
    width: 100%;    height: 100%;    position: absolute;    left: 0px;    top: 0px;    background-color: rgba(0, 0, 0, 0.5);    display: block;  }

  .content
  {
    width: 500px;
    height: 500px;
    margin: 5% auto 0 auto;
    background-color: rgba(0, 0, 0, 1);
    border-radius: 30px;
  }
</style>


Yep, here's that black square. Impossible to miss.


We introduce a new div inside that one, and style it using the CSS class cigarette.
<div id="overlay">
  <div class="content">
    <div class="cigarette">
    </div>
  </div>
</div>


This is how we style cigarette. It is a long vertical rectangle set in the middle of its parent, content. Think of it as a holder for all the divs that will make up the components of the cigarette.
.content
{
  width: 500px;
  height: 500px;
  margin: 5% auto 0 auto;
  background-color: rgba(0, 0, 0, 1);
  border-radius: 30px;
}

.cigarette
{
  width: 50px;
  height: 400px;
  margin: 10% auto 0 auto;
}


All in position, so far!



Now we'll do the individual parts of the cigarette. We have two divs - the first styled using body and the second styled using butt.
<div class="cigarette">
  <div class="body">

  </div>

  <div class="butt">
  
  </div>

</div>


body takes up full width but only 300 out of the 400 pixels on offer. There is explicitly no background.
.cigarette
{
  width: 50px;
  height: 400px;
  margin: 10% auto 0 auto;
}

.body
{
  width: 100%;
  height: 300px;
  background-color: none;
}


butt, similarly, takes up fill width and has only 100 pixels height. We have as its background a linear gradient, going from brown, yellow, then brown again.
.cigarette
{
  width: 50px;
  height: 400px;
  margin: 10% auto 0 auto;
}

.butt
{
  width: 100%;
  height: 100px;
  background-color: rgba(255, 170, 0, 1);
  background: linear-gradient(90deg,rgba(255, 170, 0, 1) 0%, rgba(255, 255, 170, 1) 50%, rgba(255, 170, 0, 1) 100%);
}


.body
{
  width: 100%;
  height: 300px;
  background-color: none;
}


This is what it looks like now. We'll be working on the empty-looking div next.


In body, we nest two more divs. Instead of classes, they will have ids because this will make them easier to manipulate via JavaScript later. The ids are empty (because that div represents "empty" space) and burnable (because it's the part of the cigarette that gets "burned").
<div class="body">
  <div id="empty">

  </div>

  <div id="burnable">

  </div>

</div>


body has 300 pixels in height, so empty takes up 50 and burnable takes up 250. burnable has a linear background that goes from light grey, white, then light grey again.
.body
{
  width: 100%;
  height: 300px;
  background-color: none;
}

#empty
{
  width: 100%;
  height: 50px;
}

#burnable
{
  width: 100%;
  height: 250px;
  background-color: rgba(255, 255, 255, 1);
  background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}


So now we have an unlit cigarette.


In burnable, we have a div styled using the tip CSS class.
<div id="burnable">
  <div class="tip">

  </div>

</div>


tip has a height of 5 pixels and takes up full width; that's all there is to it.
#burnable
{
  width: 100%;
  height: 250px;
  background-color: rgba(255, 255, 255, 1);
  background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}

.tip
{
  width: 100%;
  height: 5px;
}


In that div, let's add three divs, each styled using the ember CSS class.
<div id="burnable">
  <div class="tip">
    <div class="ember"></div>
    <div class="ember"></div>
    <div class="ember"></div>  </div>
</div>


ember is floated left and has an orange background.
.tip
{
  width: 100%;
  height: 5px;
}

.ember
{
  float: left;
  background-color: rgba(250, 150, 0, 1);
}


However, height and width vary according to their order. There are three divs in there, styled using ember, and we use the nth-of-type pseudoselector, passing in either "odd" or "even" and then adjusting the width and height accordingly. The middle (or second) "ember" is supposed to be the largest one.
.ember
{
  float: left;
  background-color: rgba(250, 150, 0, 1);
}

.ember:nth-of-type(odd)
{
  width:15px;
  height:3px;
}

.ember:nth-of-type(even)
{
  width:20px;
  height:5px;
}


Just for fun, let's add a bit of animation. This is totally superfluous. The animation name is emberglow and we want it to last for 2 seconds, run forever, and alternate smoothly between states. You'll see that for emberglow, I basically have background go from orange to red.
.ember
{
  float: left;
  background-color: rgba(250, 150, 0, 1);
  animation-name: emberglow;
  animation-duration: 2s;
  animation-iteration-count: infinite;
  animation-direction: alternate;

}

.ember:nth-of-type(odd)
{
  width:15px;
  height:3px;
}

.ember:nth-of-type(even)
{
  width:20px;
  height:5px;
}

@keyframes emberglow
{
  from { background-color: rgba(250, 150, 0, 1); }
  to { background-color: rgba(255, 0, 0, 1); }
}


There you go, a glowing tip! I feel like there's a filthy joke I could insert here, but let's move on...


In empty, we insert divs. Several divs. But let's just start with three. Style them using the CSS class smoke.
<div id="empty">
  <div class="smoke"></div>
  <div class="smoke"></div>
  <div class="smoke"></div>

</div>


Give each of them a random height, weight, margin-top and margin-left property. Do bear in mind that the effective width of empty is 50 pixels due to its parents, so the sum of margin-left and half of width shouldn't exceed 50, otherwise we'll have an overflow problem. Similarly, if we subtract half of width from margin-left, it should not be less than 0. width and height should be the same. You can have negative values for margin-top if you want the divs to overlap.
<div id="empty">
  <div class="smoke" style="width:15px;height:15px;margin-top:5px;margin-left:25px;"></div>
  <div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
  <div class="smoke" style="width:18px;height:18px;margin-top:0px;margin-left:5px;"></div>
</div>


smoke has a translucent white background and a nice fuzzy grey outline by way of the box-shadow property.
#empty
{
  width: 100%;
  height: 50px;
  background-color: rgba(0, 0, 0, 1);
}

.smoke
{
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
}

#burnable
{
  width: 100%;
  height: 250px;
  background-color: rgba(255, 255, 255, 1);
  background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}


And that's how the first three divs would look like.


Add several more! Make sure width, height, margin-top and margin-left properties vary.
<div id="empty">
  <div class="smoke" style="width:15px;height:15px;margin-top:5px;margin-left:25px;"></div>
  <div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
  <div class="smoke" style="width:18px;height:18px;margin-top:0px;margin-left:5px;"></div>
  <div class="smoke" style="width:10px;height:10px;margin-top:-5px;margin-left:30px;"></div>
  <div class="smoke" style="width:15px;height:15px;margin-top:0px;margin-left:20px;"></div>
  <div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:15px;"></div>
  <div class="smoke" style="width:8px;height:8px;margin-top:-5px;margin-left:13px;"></div>
  <div class="smoke" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
  <div class="smoke" style="width:8px;height:8px;margin-top:0px;margin-left:22px;"></div>
  <div class="smoke" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
  <div class="smoke" style="width:20px;height:20px;margin-top:-5px;margin-left:15px;"></div>
  <div class="smoke" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
  <div class="smoke" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
  <div class="smoke" style="width:8px;height:8px;margin-top:0px;margin-left:30px;"></div>
  <div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
  <div class="smoke" style="width:8px;height:8px;margin-top:0px;margin-left:10px;"></div>
  <div class="smoke" style="width:15px;height:15px;margin-top:0px;margin-left:15px;"></div>
  <div class="smoke" style="width:20px;height:20px;margin-top:-5px;margin-left:10px;"></div>
  <div class="smoke" style="width:8px;height:8px;margin-top:-2px;margin-left:30px;"></div>
  <div class="smoke" style="width:10px;height:10px;margin-top:-2px;margin-left:15px;"></div>
  <div class="smoke" style="width:15px;height:15px;margin-top:-0px;margin-left:20px;"></div>

</div>


And you'll see their red outlines even if they're not visible after the glowing tip otherwise.


Now, at random, have each div styled using smoke1 or smoke2.
<div id="empty">
  <div class="smoke smoke1" style="width:15px;height:15px;margin-top:5px;margin-left:25px;"></div>
  <div class="smoke smoke1" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
  <div class="smoke smoke2" style="width:18px;height:18px;margin-top:0px;margin-left:5px;"></div>
  <div class="smoke smoke1" style="width:10px;height:10px;margin-top:-5px;margin-left:30px;"></div>
  <div class="smoke smoke2" style="width:15px;height:15px;margin-top:0px;margin-left:20px;"></div>
  <div class="smoke smoke2" style="width:20px;height:20px;margin-top:-2px;margin-left:15px;"></div>
  <div class="smoke smoke1" style="width:8px;height:8px;margin-top:-5px;margin-left:13px;"></div>
  <div class="smoke smoke2" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
  <div class="smoke smoke1" style="width:8px;height:8px;margin-top:0px;margin-left:22px;"></div>
  <div class="smoke smoke2" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
  <div class="smoke smoke2" style="width:20px;height:20px;margin-top:-5px;margin-left:15px;"></div>
  <div class="smoke smoke1" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
  <div class="smoke smoke2" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
  <div class="smoke smoke1" style="width:8px;height:8px;margin-top:0px;margin-left:30px;"></div>
  <div class="smoke smoke2" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
  <div class="smoke smoke2" style="width:8px;height:8px;margin-top:0px;margin-left:10px;"></div>
  <div class="smoke smoke1" style="width:15px;height:15px;margin-top:0px;margin-left:15px;"></div>
  <div class="smoke smoke2" style="width:20px;height:20px;margin-top:-5px;margin-left:10px;"></div>
  <div class="smoke smoke1" style="width:8px;height:8px;margin-top:-2px;margin-left:30px;"></div>
  <div class="smoke smoke2" style="width:10px;height:10px;margin-top:-2px;margin-left:15px;"></div>
  <div class="smoke smoke1" style="width:15px;height:15px;margin-top:-0px;margin-left:20px;"></div>
</div>


This is for more animation. These CSS classes each call an animation, with different durations. However, both of these animations will run forever, and alternate back and forth.
.smoke
{
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
}

.smoke1
{
  animation-name: smokebubble1;
  animation-duration: 1s;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

.smoke2
{
  animation-name: smokebubble2;
  animation-duration: 3s;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}


#burnable
{
  width: 100%;
  height: 250px;
  background-color: rgba(255, 255, 255, 1);
  background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}


Here are their animations. The box-shadow and margin-left properties are animated. I won't bother showing screenshots because the range of motion is rather limited.
.smoke1
{
  animation-name: smokebubble1;
  animation-duration: 1s;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

.smoke2
{
  animation-name: smokebubble2;
  animation-duration: 3s;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

@keyframes smokebubble1
{
  from { box-shadow: 0 0 11px rgba(255, 255, 255, 1); margin-left: 26px; }
}

@keyframes smokebubble2
{
  from { box-shadow: 0 0 12px rgba(255, 255, 255, 0.8); margin-left: 24px; }
}


#burnable
{
  width: 100%;
  height: 250px;
  background-color: rgba(255, 255, 255, 1);
  background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}


In the JavaScript, we define the object idleCounter. In it, there are the properties lastActivity and msToPopup. lastActivity is a timestamp and defaults to null. msToPopup is an integer, defaults to 5, and defines the number of seconds of idle time encountered before the popup happens.
<script>
  var idleCounter =
  {
    lastActivity: null,
    msToPopup: 5
  };

</script>


We then have the method, startTimer(). What it does is set the observeLastActivity() method to run every second.
<script>
  var idleCounter =
  {
    lastActivity: null,
    msToPopup: 5,
    startTimer: function()
    {
      setInterval(
        () =>
        {
          this.observeLastActivity();
        }, 
       1000
      );
    }
  };
</script>


And we want this method to run as soon as the page is loaded.
<script>
  var idleCounter =
  {
    lastActivity: null,
    msToPopup: 5,
    startTimer: function()
    {
      setInterval(
        () =>
        {
          this.observeLastActivity();
        },
        5000
      );
    }
  };

  window.onload = () =>
  {
    idleCounter.startTimer();
  };
</script>


Create observeLastActivity(). If lastActivity is falsy (not defined or just null) then we run the setLastActivity() method. We'll run the popup() method regardless.
var idleCounter =
{
  lastActivity: null,
  msToPopup: 5,
  popupOpen: false,
  observeLastActivity: function()
  {
    if (!this.lastActivity) this.setLastActivity();
    this.popup();
  },

  startTimer: function()
  {
    setInterval(
      () =>
      {
        this.observeLastActivity();
      },
      5000
    );
  }
};


So we have two new methods to create. Start with setLastActivity(). This basically involves setting lastActivity to the current time, then running popup().
var idleCounter =
{
  lastActivity: null,
  msToPopup: 5,
  setLastActivity: function()
  {
    this.lastActivity = new Date();
    this.popup();
  },
  observeLastActivity: function()
  {
    if (!this.lastActivity) this.setLastActivity();
    this.popup();
  },
  startTimer: function()
  {
    setInterval(
      () => 
      {
        this.observeLastActivity(); 
     }, 
     5000
    );
  }
};


Now for the popup() method. Define d as the current time. Then use the getTime() method on d and lastActivity to get the number of milliseconds since the first day of 1970, for each timestamp, and subtract to get diff. Of course d will always be greater than lastActivity, even if by a couple milliseconds.
var idleCounter =
{
  lastActivity: null,
  msToPopup: 5,
  popupOpen: false,
  setLastActivity: function()
  {
    this.lastActivity = new Date();
    this.popup();
  },
  observeLastActivity: function()
  {
    if (!this.lastActivity) this.setLastActivity();
    this.popup();
  },
  startTimer: function()
  {
    setInterval(
      () => 
      {
        this.observeLastActivity(); 
     }, 
     5000
    );
  }
  popup: function()
  {
    var d = new Date();
    var diff = d.getTime() - this.lastActivity.getTime();
  }
};


Then divide diff (which is in milliseconds) by 1000 to get the number of seconds. And create a conditional to check if diff is now greater or equal to msToPopup.
popup: function()
{
  var d = new Date();
  var diff = d.getTime() - this.lastActivity.getTime();
  diff = diff / 1000;

  if (diff >= this.msToPopup)
  {

  }
  else
  {

  }

}


Define overlay as the modal, empty as the empty div and burnable as the burnable div. This is where it gets exciting.
popup: function()
{
  var d = new Date();
  var diff = d.getTime() - this.lastActivity.getTime();
  diff = diff / 1000;

  var overlay = document.getElementById("overlay");
  var empty = document.getElementById("empty");
  var burnable = document.getElementById("burnable");


  if (diff >= this.msToPopup)
  {

  }
  else
  {

  }
}


Now if diff is greater or equal to msToPopup, we want to display the modal by setting the display property of its style object to block. By default, we will set empty's height to 50 pixels and burnable's height to 250 pixels. If not, we hide overlay.
popup: function()
{
  var d = new Date();
  var diff = d.getTime() - this.lastActivity.getTime();
  diff = diff / 1000;

  var overlay = document.getElementById("overlay");
  var empty = document.getElementById("empty");
  var burnable = document.getElementById("burnable");

  if (diff >= this.msToPopup)
  {
    overlay.style.display = "block";
    empty.style.height = "50px";
    burnable.style.height = "250px";

  }
  else
  {
    overlay.style.display = "none";
  }
}


Here's the fun part. If diff is greater or equal to twice msToPopup, set empty's height to 80 pixels and burnable's height to 220 pixels. It will still add up to 300 pixels. The effect is that if more idle time has passed, empty will be taller and burnable will be shorter.
popup: function()
{
  var d = new Date();
  var diff = d.getTime() - this.lastActivity.getTime();
  diff = diff / 1000;

  var overlay = document.getElementById("overlay");
  var empty = document.getElementById("empty");
  var burnable = document.getElementById("burnable");

  if (diff >= this.msToPopup)
  {
    overlay.style.display = "block"; 
    empty.style.height = "50px";
    burnable.style.height = "250px";

    if (diff >= (this.msToPopup * 2))
    {
       empty.style.height = "80px";
       burnable.style.height = "220px";
    }
  }
  else
  {
    overlay.style.display = "none";
  }
}


And so on, and so forth. I've set a few cases here and you should feel free to add more.
popup: function()
{
  var d = new Date();
  var diff = d.getTime() - this.lastActivity.getTime();
  diff = diff / 1000;

  var overlay = document.getElementById("overlay");
  var empty = document.getElementById("empty");
  var burnable = document.getElementById("burnable");

  if (diff >= this.msToPopup)
  {
    overlay.style.display = "block";
    empty.style.height = "50px";
    burnable.style.height = "250px";

    if (diff >= (this.msToPopup * 2))
    {
       empty.style.height = "80px";
       burnable.style.height = "220px";
    }

    if (diff >= (this.msToPopup * 3))
    {
       empty.style.height = "100px";
       burnable.style.height = "200px";
    }

    if (diff >= (this.msToPopup * 5))
    {
       empty.style.height = "200px";
       burnable.style.height = "100px";
    }

    if (diff >= (this.msToPopup * 10))
    {
       empty.style.height = "250px";
      burnable.style.height = "50px";
    }

  }
  else
  {
    overlay.style.display = "none";
  }
}


And while we're at it, disable the red outline.
div { outline: 0px solid red; }


When you first refresh the browser, there should be no modal because the popup() method detects that 5 seconds of inactivity have not passed, so the modal remains invisible. Wait for 5 seconds, and it should come up!


At 10 seconds, the cigarette grows shorter.


At 15 seconds...


At 25 seconds...


At 50 seconds...


For the final touch, add these two lines. This ensures that if you move the mouse or press a keyboard button, setLastActivity() gets run and will result in the modal disappearing until another 5 seconds of inactivity passes.
window.onload = () =>
{
  idleCounter.startTimer();

  document.body.addEventListener("keypress", ()=> { idleCounter.setLastActivity(); });
  document.body.addEventListener("mousemove", ()=> { idleCounter.setLastActivity(); });

};


Well done!

We just implemented a browser screen saver. Cool, right?!

Don't be idle now!
T___T