I’ve been planning on doing a web comic for a couple of years now, keeping notes and ideas for strips and mentally designing the web app. I started by doing research and trying to find out-of-the-box solutions in order to save time, only to realize that WordPress + plugins seemed to be the only feasible solution (and maybe that I was using the wrong search keywords).

But since a CMS to handle simple tasks is as inefficient as using a katana to kill ants, I decided to go with Node.js.

Cutting to the chase and avoiding the nitty-gritty aspects, assuming you know your way around coding, I used Express to handle the routing:

var comics = require('./comics.json');

app.get("/:id(\\d+)", function(req, res){
  var id = Number(req.params.id);
  if (id > comics.length)
    res.render('404', { status: 404, url: req.url });
    res.render('index', {comic: comics[id - 1], previous: previous, next: next});

(//d+) ensures that the specific route is applied only to numbers. View engine of choice is Jade/Pug. Rendering a 404 page in case of a wrong ID:

res.render('404', { status: 404, url: req.url });

In any other case, “index” is rendered and a comic object is sent. I used the same template I have on my blog, which uses Jekyll to serve static content. Comics are simply stored in a JSON file - comics.json - in the following format:

      "num": "1",
      "title": "Ice Destroyer",
      "imagename": "icebreaker.jpg",
      "desc" : "With your brains and my beauty, our children will be exceptional",
      "alt" : "Ironically enough, there was a sperm bank that was allegedly accepting donations only from Nobel Prize laureates",
      "date": "Apr 07, 2017"
      "num": "2",
      "title": "Title 2",
      "imagename": "johndoe.jpg",
      "desc" : "A description",
      "alt" : "An alternative description",
      "date": "Apr 07, 2042"

Then index.pug replaces all the necessary parts with the comic content. Facebook meta properties are filled:

property="og:title" content="#{comic.title}"
property="og:description" content="#{comic.desc}"
property="og:type" content="website"
property="og:url" content="https://comic.stefki.com/#{comic.num}"
property="og:image" content="https://stefki.com/comic/strips/#{comic.imagename}"

And then the parsed comic object data are used with the necessary HTML tags:


All I need to do to add a new comic is to add a new image in the images folder:

app.use("/images", express.static(__dirname + '/public/views/images'));

and add the details to the comics.json file.

Some back-end related extras; Robots.txt is served within Node:

app.get('/robots.txt', function (req, res) {
    res.send("User-agent: *\nAllow: *");

And since browsers are in love with favicons and in order to avoid multiple requests, I used serve-favicon to serve it via Node:

var favicon = require('serve-favicon');
var path = require('path');
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

Designing a comic is not easy, especially since I have almost non-existent graphic design skills, which means that I needed to start from somewhere. The best I could find was cmx.IO. Combining it with google searching “female hair comics” and GIMP to further edit the strips, I’ve managed to at least start putting my ideas to strips.

I even used XKCD as a starting point for the way it handles comic navigation and even the positioning of navigational controls. I feel guilty but hey, one needs to start somewhere.