How I made Reddit in the Console

neal

Neal Agarwal

Posted on June 6, 2019

How I made Reddit in the Console

Sometimes it's fun to take technologies and use them in ways they obviously weren't meant to be used. Besides being fun, it also helps you learn about the quirks of the technology you're using.

The goal of this project for me was to learn more about the console and some of the lesser known features by re-making Reddit. Even though I've been console.logging my whole life, I found several console features I never even knew about through this process!

Getting the data

I chose to implement Reddit instead of a different social media site because it's amazingly easy to get data from Reddit.

Want to get the json data for /r/all? Simply append .json to the url:
https://www.reddit.com/r/all.json

Beautiful, thanks Reddit.

Now that we have an API endpoint, we can fetch the data:

function getPosts(currentSub){
  let url = "https://www.reddit.com/r/" + currentSub + ".json";
  fetch(url)
    .then(function(response) {
      return response.json();
  })
  .then(function(json) {
    parsePosts(json["data"]);
  });
}
Enter fullscreen mode Exit fullscreen mode

Our getPosts function will take a subreddit name so we can fetch posts from any subreddit in the console!

Printing images

Reddit is mostly images, and so our console version of Reddit should support images too!

Going into this project I knew that it was possible to print images to the console, but I had no idea how.

After some research I found out: It's black magic.

It turns out that if you put "%c" in your console.log message, then you can pass a second parameter with styles that will be added to the message.

For example:
console.log('%c IM HUGE', 'font-size: 48px;');

will show up as:

So how do we use this feature to print images? The answer is the background-image property! But images don't behave as expected in the console. For instance the following

console.log('%c', 'background-url=url(./walrus.jpg) no-repeat;');
Enter fullscreen mode Exit fullscreen mode

Will appear as:

To get the image to display properly, we'll have to add padding to our style.

console.log('%c ', 'padding: 125px; background:url(./walrus.jpg) no-repeat;');
Enter fullscreen mode Exit fullscreen mode

Now it will display correctly:

Now this is great, but we can't hardcode the padding on the styles because the images from Reddit will have different dimensions. To fix this we'll need to add different padding on each image depending on the image size.

We can do that with some Javascript:

function logImage(url) {
    var img = new Image();

    img.onload = function() {
        let width = this.width;
        let height = this.height;

        //Resize if needed
        if (this.width > 600) {
            width = 600;
            height = 600 * (this.height / this.width);
        }

        var style = getStyle(width, height);
        console.log("%c", style + "background: url(" + url + ");");
    };

    img.src = url;
}


function getStyle(width, height) {
    return "padding: " +
               Math.floor(height/3) + "px " +
               Math.floor(width/2) + "px; line-height: " +
               height/3 + "px;"
}
Enter fullscreen mode Exit fullscreen mode

We also scale the image so that the maximum width of the image is 600px which provides a better viewing experience. Also line-height is added to make sure that the image stays centered.

Next, let's print the actual images from Reddit.

Our Reddit JSON data contains a URL for each post. Using that we can check if the URL is an image by checking if it contains an image extension (.png, .jpg, etc). If it does, then we console.log the image.

It even works for gifs!

To add more context, let's add more information like the author and upvote count.

console.log('%c ' + "/r/" + postData["subreddit"] + 
                    " 路 Posted by u/" + postData["author"] +
                    ""  + postData["score"] + " upvotes", 
                    "font-size: 14px;");
Enter fullscreen mode Exit fullscreen mode

Here's what it looks like now:

Beautiful! Maybe this is how Reddit was meant to be experienced.

All that's left is to add the comments.

Adding the Comments

Sadly the original API endpoint we called does not include comments. So we will have to include another API call for each post to get the comments. But thankfully Reddit makes this process easy too. We just have to use the permalink given in the first API call to call another endpoint:

function getComments(permalink, done){
  let url = "https://www.reddit.com" + permalink + ".json";

  fetch(url)
    .then(function(response) {
      return response.json();
  })
  .then(function(json) {
     parseComments(json);
     done();
  });
}
Enter fullscreen mode Exit fullscreen mode

And just like that, we're able to get comments for all our posts!

Now we just have to figure out to display them. We could simply console.log all the comments, but considering a Reddit post can get hundreds of comments, simply printing all the comments would be a bad user experience.

Luckily, the console has another lesser known feature. Using console.group() and console.groupEnd(), we can create collapsable groups inside the console.

For example:

console.group("This is a group");
console.log("I'm inside the group!")
console.groupEnd();
Enter fullscreen mode Exit fullscreen mode

Will look like this inside the console:

Using this, we can now make our comments collapsed by default:

But wait that's not all! The console allows you to nest groups inside each other. So we can actually display the Reddit comments in a threaded fashion just like the real Reddit!

Here's how it works:

function addComment(comment){
  if(!comment["body"]){
    return; 
  }
  console.group(comment["author"] + "" + comment["score"] + " upvotes");
  console.log(comment["body"]);
  if(comment["replies"]){
    parseReplies(comment["replies"]["data"]["children"]);
  }
  console.groupEnd();
}

function parseReplies(replies){
   for(let x = 0; x < replies.length; x++){
     addComment(replies[x]["data"]);
   }
}
Enter fullscreen mode Exit fullscreen mode

For each comment, we create another console group with the author name and the number of upvotes for that comment. We then recursively do the same thing for all the replies to that comment and only call console.groupEnd() once all the replies have been added.

The result looks just like normal Reddit comments, and comment threads are even collapsible!

Putting it all together here's the final product:

Click here to see the full code
And click here to see it in action!

This project was a blast and I learned more about the console than I thought I ever would. Feel free to remix it and add more features.

Also if you like silly & fun projects, I have more on my Twitter!

馃挅 馃挭 馃檯 馃毄
neal
Neal Agarwal

Posted on June 6, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

How I made Reddit in the Console