Eagle eyed JungleDragon followers may have noticed the little reputation chart on the user profile screenshots that I shared earlier:

To really appreciate this component, you have to see it *and hover it* in action. That’s why I cooked up a little demo site for you to try it (click to visit the demo site):

Like it? Read on…

The purpose of this chart is to show trend data, in this example concerning a user’s reputation. Each plot point in the chart concerns an event that led to a karma change, be it positive or negative. When you hover a plot point, you see the karma change, but also a summary of the event took place along with the date.

And, the very best thing about it is that is not Flash based! This is Javascript only.

How it’s made

This chart is not written as a generic component, so I will not detail every line of code. Still, I will give away some clues should you want to do a similar thing.

The main thing to know is that this chart relies on the Raphael javascript library. Raphael uses SVG rendering (or VML in the case of IE) to draw objects on a canvas. Raphael lets you interact with the vector objects using Javascript.

By sheer accident I ended up at this chart demo one day. I liked it a lot and decided to customize it to my needs. That was pretty tough actually, since the original demo’s code is not documented, slightly messy and pretty much hard coded. I changed several things:

  • Scale transformation since the demo could only handle values between 0 and 100
  • Customization of the grid and general look & feel
  • Extra logic to differentiate between positive and negative plot points
  • Custom placeholder and data parsing for detailed event information when you hover a plot point

The resulting chart, which you can see in the demo, works perfectly for my needs. Basically it works like this:

  • The HTML on the page has a hidden table containing the data. The data is split into tfoot and tbody tags.
  • The cells in the tbody tag contain the actual numbers which are used to draw the plot points
  • The cells in the tfoot tag contains the event description that match with each plot point
  • The script parses the table, builds up internal arrays for the data and renders the grid, chart and such
  • Upon hovering a plot point, it will look up the details in the internal array and display it in the bottom placeholder.

There’s a whole lot of coordinate logic going on, but the essence of it all is simple.

Two final remarks:

  • Although this demo should work fine in IE, it does show that IE’s javascript engine is slow compared to Chrome, Firefox and Safari. There are a lot more hickups in IE.
  • If you are willing to dive into the code, you are free to take this demo and customize to your own needs. However, I would advise considering alternatives as well. For example, there is a dedicated Raphael javascript library for charting called gRaphael.

Do you like it?

Earlier this year I demonstrated how I dramatically improved the upload experience in JungleDragon. That story reveals that there is quite a lot going on when you upload an image. Today, I experienced that the opposite was true as well: deleting an image from JungleDragon is complex.

Complex is perhaps an overstatement, but there’s quite some things to deal with:

  • There are a lot of relations in the database to an image record, for example comments, votes, revisions, favorites and tags. Therefore, I had to implement cascading deletes on the foreign keys to really clean up everything related to that image.
  • Tag records have to dealt with seperately. I can delete the mapping between the tag and the image, but not the tags of the images to be deleted, because there may be other images associated with it.
  • The karma log, or activity stream as I demonstrated earlier, is like a historic record of activity. I cannot just delete historic entries because the image is deleted, that would make the overview inconsistent. Therefore, I had to cache some image data in the karma log, and for deleted images not show the preview of the image anymore (because it is deleted), instead I’m showing a “deleted” icon.
  • Any karma that the user has earned via the image to be deleted has to be taken away. I had to make a roundtrip through all code to see if I implemented this correctly, because a karma “leak” would be a disaster to the reputation system.
  • The actual image files have to be deleted, depending on where they are stored (local, Amazon S3, or both). I also implemented a DELETE_MODE setting in the back-end. When set to “hard” it will delete image files, when set to “soft” it will delete the database records, but not the image files.
  • All of the above has to work like a transaction: any failure should mean a complete rollback, and only a commit is made when every atomic part is successful.

This is just a really long way of saying: deleting an image is done, was hard, but I’m happy it is behind me now. There is little to demonstrate about deleting an image, but this partial screenshot shows what happens in the karma log of a user when the image in question is deleted:

I could have gone for more sophistication in soft delete mode, for example by always saving at least the thumbnail of a deleted image, but for now I long for simplicity, it is complex enough as it is.

In this post I will explain the JungleDragon button bar, which is a button bar that applies to an image. I will detail how it works, why it was designed that way and how it was made. Perhaps there is something you can reuse.

How it works

This is  what the button bar looks like below an image:

The button bar is a context menu of actions one can apply to the image above it. Each button has a number of states. For one, there is the invisible state. Perhaps silly to mention, but the button bar will only show a button when it is relevant. This can be based on the authorization of the user, but also the state of the image.

Next, there is the hover state, where a user hovers over a particular button:

There is also a disabled state, for example when a user has already applied the action. An extra explanation is given using a tooltip:

Finally, there is the active state. It looks similar to the hover state but the interaction is different. In this example, the user has clicked the image to toggle zoom mode. Although the zoom button was not clicked, it was activated indirectly:

Design considerations

There are many ways to design a button bar like this, so hereby I will explain my reasoning behind it. We web developers tend to default on standard icon sets such as Silk for our icons. As nicely as those icons look, I still decided to chose a different path, here’s why:

  • I believe that content matters most, and that content should stand out, not icons. Perhaps this is a personal preference but I dislike how bright colorful icons curse with an otherwise consistent user interface style. especially when most of your audience will simply view content and not apply any action on it.
  • Look at real world icons, such as traffic signs, airport signs and such and you will see that most of them are monochrome, using two colors with a high contrast for optimal legibility.
  • Icons are ambiguous. A star icon could mean “rate this” or “add to favourites”. What is the agreed icon for a “history”? To be sure that the user understands the icon, it makes sense to supplement it with text.
  • Standard icons sets typically do not have different states (normal, hover, disabled, active).

All in all I believe I had good reasons to go the custom path, so let’s see how it was done.

How it’s made

It’s simple to create a button bar like this, but there’s some interesting clues to share nevertheless…

For one, I did not create the icon illustrations myself. I grabbed them from the jQuery UI theme page. For each icon that I needed, I cut out just the icon (not the box around it) and paste it into my button box template in Photoshop. If that page did not provide the icon I needed, I would use an icon search engine for my needs.

Next, the text. It would be ideal if we could make the icon button text part of the HTML. At the same time, we do not want it to take up too much space. Unfortunately, browsers suck at rendering fonts at such a small size. Therefore, the texts are part of the icon images. In order to make the texts look as clear and crisp as they do at such a small font size, we need to use a pixel font. I used PF Arma Five for it. It is important to realize that pixel fonts are designed for one size only, in this case 8px. Therefore, do not apply any effects on it in Photoshop and do not change the font size.

Finally, integrating the result into the web front-end. The default way would be to simply cut out the images and refer to them in our web pages. However, given the number of buttons and their states, this could easily lead to a dozen or more HTTP requests. An additional problem is that when we do not preload all states, the user may have to wait for an image to be loaded when they hover a button.

This is easily avoided by using a spriting technique. Instead of loading dozens of icon images, I only load one:

Next, I use CSS background image positioning to show the correct part of the icon. The result is a single HTTP request for the entire button bar and no load times for the hover states. If you are new to this technique, I can recommend this introduction.

Main buttons

Whilst on the subject of buttons, I want to share the improved look of the action buttons I use in forms:

It is created using this technique. I love the result. What’s also nice is that the color is controlled in CSS. The shadows and shine effect are an overlay. It also looks the same in Internet Explorer, which is notoriously bad in button rendering. However, there are downsides to this technique:

  • The markup is not pure. It uses a href links instead of input or button HTML elements. Unfortunately, there is no other way to make a button look this nice in IE.
  • One consequence of not using a normal submit button is that submit behavior that you rely on will stop working. For one, you will have to use Javascript to submit the form upon clicking the link. If Javascript is disabled, you’re out of luck.
  • Another consequence is that hitting ENTER to submit a form will not work. A cheap trick to work around this is to include a input button or image of type submit, and make it invisible through styling.

I’m still in doubt about this one. It feels dirty, even though JungleDragon needs Javascript to work anyway. Still, the result is beautiful. Once again it is Internet Explorer who makes the web miserable.

So, what do you think about my approach towards buttons? Do my design considerations make sense?

You would think that since JungleDragon is about sharing images, the majority of my development work is in images. That’s not true though, the vast majority of my development work for JungleDragon evolves around users. Their account. Their content. Their interactions. Their friends. Their reputation.

The whole social glue of JungleDragon is scattered across the application, but for now I will focus on the user profile part. The user profile is the place where everything concerning a single user comes together. What I will show you in this screenshot tour is already the third version of how I implemented user profiles. I threw out many hours of work twice in a row because I was not satisfied with the result. Now I am, although some polishment still remains.

Your own profile

This is what your own profile looks like when you are logged in. Click to enlarge:



As you can see, it has somewhat of a portal design style. We’re currently looking at the home page of our own user profile, yet there are many other pages which can be accessed using the tabs. The left column shows the tab specific content. The right column is the user context, it summarizes basic data about the user and stays consistent no matter which tab you click. So all in all we have a dynamic left column, a static right column, and tabs to move between specific sections of the user profile.

User activity streams

Now, let’s talk about the most powerful aspect of user profiles in JungleDragon: activity streams. JungleDragon records all activity around any user, both active activities (you uploading an image, you commenting, voting, etc) and passive activities (another user voting on your image or comment, another user moderating your image, etc).

The benefit of having all this information is that we can then show an activity stream of a user as can be seen in the screenshot above. An activity stream is similar to a user profile on Twitter, it shows a list of user activities, newest first. With this in place, one can instantly see what a user or friend is up to. No need to click through all tabs to see if anything happened. Cool.

What I think is even more interesting is the way in which it is implemented. A few notable details:

  • The karma graph. This is a chart like control that shows the reputation trend of this user. You have to see it to believe it really, you’d think it is Flash but it is plain Javascript with all kinds of cool hover tricks. In essence, this is a picture of how this user is doing, reputation wise.
  • Activity context. A system like twitter only has one user event: posting a message, but JungleDragon has 20 user events. So how do you distinguish between these event types? Context is the answer. The image that you see on the left of each activity row represents the context. If you do something around a specific image, it will show that image, if you friend someone, it will show that user’s icon, if you get promoted to a higher class, it will show the new class image. And so on…
  • The middle column of an activity row describes the event that took place along with links to the object or users in context, if applicable.
  • The right column of an activity row show the karma reward associated with the event, consistent with the plot points in the chart.

All in all I think this makes for quite a rich activity stream. Important are also the differences in what you see. If you look at your own profile, you will see all active and passive events evolving around you. However, if you view a profile of another user, you only see their active events (click to enlarge):



Notice also that the karma chart is not visible for other users, for reasons too long to explain here.

Editing your profile

Let’s take a little break from activity streams and see how a user can edit their profile. The last tab, showing the administration icon toggles the user profile into edit mode. Alternatively, the “Manage your account” block can be used. Click to enlarge:



Profile editing is split into optimized screens for each task in order to keep it friendly. That’s why there are seperate screens for changing your avatar, email and password. The main profile edit screen is shown above. There is not much to tell about this screen other than some usability best practices I tried to follow:

  • Large labels and input boxes
  • Clear seperation between main action or cancelling
  • Each field on a new line to increase readability (exception is the birthdate field, which is a composed field)
  • Non-intrusive: only your username is required

An insignificant, but subtle little detail is what happens when you step back out of edit mode. Your “About” block will automatically assemble a natural language sentence that summarizes who you are:

See the sentence in light gray. Since none of the fields are required, it will list what it knows about you.

None of these details are designed by accident or because I think they are just cool. The whole user profile design is designed around how we socialize in real life:

  • Who are you (user details)
  • How are you (reputation)
  • What do you do (activity stream)
  • Who are your friends?

Friends

JungleDragon allows you to friend other users and on the “friends” tab of a user profile you can see who they are (click to enlarge):



Here you can see who your friends are. You can also delete individual friends or visit their profiles. Notice the little filter on top which allows you to sort the list of friends in different ways. Cute, but by far the most powerful aspect of the friends feature is the green button named “View friend activity” (click to enlarge):



This works just like the user activity streams we talked about before with two important differences:

  • It is a stream that consists of multiple users, not just one. In a single screen you can see what your friends are up to
  • Since this concerns multiple users, the left image shows the friend concerned, and the right image shows the context

Should I ever build a group function in JungleDragon, where a user can create a group of interest and others can join, I can easily make a group activity stream.

Tags

Through viewing a user tags, you can quickly see their interests (click to enlarge):



This screen does what it needs to do: show the list of tags, different ways to sort them, and of course a way to see how many images are in a tag.

Comments

From a user profile, one can see an overview of all comments made by that user:



A nice little touch is that it shows the image and image title to indicate the context of the comment. Other than that, I do not feel this is a really useful feature because most comments are part of a discussion with others. Nevertheless, it could be useful to see if a user makes meaningful comments or the opposite.

Community browsing

Most of the time, you will end up at a user’s profile through context. You see a beautiful image and notice that it was user “McCrock” who uploaded it. You click through to see what else he got. However, some users may also actively want to browse the community. For that, there is the Users screen (click to enlarge):



This single screen is powerful, it basically gives you access to the entire community:

  • Sort and browse through users by popularity, name, registration age
  • Type-ahead Ajax search if you know (part of) a user’s name
  • The list of your friends on the right

Todo

  • You may have noticed how I skipped over the “Images” and “Favorites” tabs in this blog post. The reason is that showing lists of images is still something I need to work on. It is a very important part of JungleDragon that I should implement right, and then reuse it at the many places where it is applicable.
  • Users can use tags for their uploaded content, but they can also favorite tags. This is not yet implemented at the user profile level
  • Subscribing. Both RSS and email subscriptions are not yet implemented in JungleDragon, but they will be important particularly for activity streams.

One final consideration that I have regarding user profiles is about user activity streams. I think the implementation is sophisticated, rich and somewhat original, but worry about its verbosity. It would be cool if a user can select which events to show on user profiles, but that would increase complexity for both the user and me, as an implementer.

I hope you made it all the way through this post, if so, I admire your courage. Implementing user profiles was hard. It was a ton of work, I changed my mind a lot and there are quite some complexities to it, more than you would think. I think I finally nailed it for the most part, but would like to know if you agree?

Note: I will tell you about how I created the karma chart in a dedicated blog post.

My project JungleDragon is about user-generated content, images of nature to be specific. Therefore, users need to be able to upload these images. This functionality I have completed about a year ago already, since without it, it is hard to test anything at all. The upload process is quite heavy and complex, the following sequence of events are occuring in real time:

  • User selects file to upload
  • File is transfered to the JungleDragon web server
  • A process cuts out five additional formats (thumbs and such)
  • The resulting 6 files are transferred to Amazon S3, their permanent storage location
  • Database records are updated (image table, imagefile table, tags table and user karma table)
  • The user is redirected to the newly uploaded image

Last week I was using the upload form to upload the following test image (click to enlarge):


This concerns a 1.6MB jpeg with a very high resolution of 3872 x 2592 pixels. I measured the total upload process at 24 seconds, which is awfully slow given that my client machine is on the same home network as my development server. Painful as it was, I had to do some major heart surgery to improve this.

Scheduled Amazon S3 transfer

My first assumption about the upload time was that the realtime transfer of the six files to Amazon S3 would take a lot of time. In essence we’re doing a double upload, one to the web server, and then one to the Amazon S3 server.

I decided to move the S3 transfer logic to a scheduled process, a cron job in Linux terms. The cron job looks in the database for image files that are not yet transferred (transferred = 0), makes the transfer, and then updates the image location in the database.

This brings good news: the total upload time decreased by 11 seconds, making it almost twice as fast. What’s also nice is that there is no user impact. As long as the images are not yet transferred to S3, the local images are served. Users can directly work with the uploaded image in all sizes. Once the S3 transfer is made, the image will be served from Amazon S3 but the user will not be able to tell.

Smarter resizing

It was nice to see the upload time of the test image go down from 24 seconds to 13 seconds, but it is still not good enough. I had the false assumption that the remaining 11 seconds would primarily be spent on the upload transfer. In reality, something else was wrong. My resizing logic was not efficient. This is what I did:

  • Resize from original size to large size
  • Resize from original size to medium size
  • Resize from original size to thumbnail size
  • Etc…

This way, each resize action took about 2 seconds. I changed this into a smarter way of resizing:

  • Resize from original size to large size
  • Resize from large size to medium size
  • Resize from medium size to thumbnail size
  • Etc…

This way, the total resize duration went down to about 2 seconds, and the total upload duration to about 3 seconds!

Image compression

Another small but important improvement that I made is to compress images while resizing. By compressing to 90%, the image file size goes down by a factor between 2 and 3. This is an important saving since my Amazon bill is based upon disk space and transfer bandwidth. In other words, this saves me money.

User experience

I also made two important changes to the user experience of the upload form:

  • The file input control is styled. It looks consistent with the rest of the design. This is something that cannot be done using CSS only.
  • There is now a fake upload progress bar (animated gif) whilst the user waits for the upload. It may feel like cheating, but studies suggest that users experience wait times as shorter when they see something actually happening

Image engine control

Several settings have been added to my back-end code to configure the “image engine”. With a few constants I can control:

  • Where images are stored (local only, s3 only or local_s3, which is a failover mode)
  • Whether images are transfered to s3 directly or scheduled
  • In what quality rate to store the images

Quite powerful when you think of it.

The result

The result of these improvements can be seen in this video. In the video I first upload the high resolution image, and then an image of which the resolution is optimized for the web.

You may want to view this enlarged in HD in order to follow what it happening. You should see that the upload experience is quite snappy now.

Conclusion

Making these improvements was painful and took quite some long nights but the results are there:

  • The total upload duration is speed up by a factor 8
  • The resulting file size is between 30% and 50% lower than before (depending on the quality rate)
  • The form looks better and gives better feedback
  • I can easily control key aspects of the image engine during runtime

For me, I learned two lessons as a result of this improvement battle:

  • Don’t assume. Know. Measure and know how things work at the lowest level.
  • Persist. Do not give up until you have the result you want.

I’m absolutely thrilled with this improvement. I can’t stop uploading images to JungleDragon :)

In this post I will demonstrate the two ways in which you can zoom into images uploaded to JungleDragon.

First, a little explanation of what happens when you upload a new image to JungleDragon:

  • Original image gets uploaded to the JungleDragon web server
  • A script runs to intelligently resize the image to different formats:
    • Square: Tiny square thumb
    • Small: Small thumb with aspect ratio respected
    • Medium: Normal size used to display image on image page
    • Large: As the word says, larger than medium :)
    • Original: The untouched, original format
  • The resulting images are transferred into Amazon S3
  • Exif data is retrieved from the original
  • Database records are created for the new image (one main image entry and multiple imagefile entries). When applicable, tag records are created too. Finally, karma is rewarded to the user and their karmalog is updated.

With this in mind, let’s have a look at how to zoom in JungleDragon:

View image formats

The simple, static kind of way to look at different image sizes is to hit the button “View sizes” in the image button bar:

This will open the “View Sizes” page (click to enlarge):



From this page, one can select the size to view using the tabs. Additional options include a download button (that downloads the size on screen) and an embed panel, which spits out code to embed the current image size on an external website. Comments, votes, tags and all other controls normally associated with an image are stripped from this page to keep it simple.

Image live zoom

The second way to zoom into an image is to use the live zoom function. This short video demonstrates the effect:

This is what happens in the video:

  • User has a normal image page open (displaying the medium sized format)
  • User clicks on the image
  • Large image is loaded in the background (takes about 0.5 sec in the video so hard to see)
  • User hovers over portions of the image and sees increased detail as taken from the larger size image
  • User hovers out of the image and sees the original, medium sizes image again

How it’s made

Perhaps a little bit of a gimmick, but I find it a nice, unobtrusive effect, since users actually have to click to activate zoom mode. You are probably wondering how this is implemented. Here’s how:

  • Download MagicZoom (license is only 29£)
  • Install the JS library into your project and refer to it on your page
  • Install the CSS file into your project (or integrate the rules into your main CSS file)

Next, create the appropriate markup on your page. This will consist  of a normal image tag which points to your medium sized image, surrounded by a “a href” which points to your larger image size. To set the options of the zoom plugin, the rel attribute of the a tag is used:

<a href="address to your large image"
class="MagicZoom"  rel="click-to-initialize:true;click-to-activate:true;
zoom-position:inner;zoom-fade:true;">
<img src="address to your smaller image" />
</a>

As you can see, I’m using an inner zoom mode, which overlays the original image with the zoomed one. There are many ways to customize the behavior though. A plugin that just works, I love it. Do you?

Two weeks ago, I demonstrated how I integrated a number of capabilities into the JungleDragon class system. The basic idea being that if you move up in the food chain, you get more power, for example more influence in voting, or the ability to edit image metadata of others:

In the screenshot above you see that the Mountain Gorilla, one of the highest classes in JungleDragon, has the ability to edit image metadata (such as an image title, description and tags) of images uploaded by other users. The idea behind this concept is that high class users (who have proven to be of great added value) get moderation power and that this improves data quality.

By providing this capability only to high class users, my ideas was that it offloads data moderation to the community itself. JungleDragon should not only be user-generated, it should also be user-moderated. Then came in a comment from a certain Joe:

“I find this to be a very interesting concept. In fact, you will get the same dynamic as in wikipedia. However, be sure to keep versioning of the information changed because someone could change something that was correct. So the author or a super-moderator could always decide to roll-back.”

My initial thought was that this is a good point. However, it would be a lot of work to implement. Plus, I kind of assumed that high class users will make image edits that are good enough to not be rolled back. I gave the idea some thought for a few days and then came to the conclusion that I do need to implement this. The implication of this decision is large, I will explain why later on. First, allow me to demonstrate how I built image versioning and what it looks like.

The scenario

On this screenshot tour I will demonstrate the following scenario:

  • User 1, called “fchristant” uploads an image and applies basic metadata
  • User 2, called “jungle” edits and enriches the metadata of the image
  • User 1 does not like the edit made by User 2 and rolls back the change

Enough talk. Let’s see how. First, “fchristant” has uploaded an image. He has only filled out the image title field, which is the only required field. Here’s what it looks like:

Zooming in a little on the right sidebar, we see the upload timestamp and user:

Now, we log in as user “jungle” and visit the same image page. “Jungle” clicks the “Edit details” button in the image button bar:

The edit screen is presented and user “Jungle” decides to enrich the image data by supplying a description and some tags:

An noteworthy detail is that the “License” field can only be edited by the image owner, therefore it is disabled for editing for user “Jungle”. Anyway, after saving this edit and viewing the sidebar of the image page, we now see this:

Cool. Image data enriched by another user. Jungle (who made the edit) hits the “view history” button to see his change being recorded:

This will present the image history screen:

Perhaps a bit hard to see, but this screen is layed out as follow. On the right is the live image data, the current version that is public. On the left you see a list of image versions, sorted by newest first. We see that there are two versions, the first one being uploaded by “fchristant”, and the edit made by “jungle”. We can even see exactly what was changed (which field) and which version is the live version.

Hopefully, in most scenarios this would be the end of it.  The change is considered an improvement and all is good. However, let’s finish our story. In this case, the original image owner, “fchristant”, does not like the edit made by Jungle. So we’re logging back in as “fchristant” and have a look at the image history screen once more. Here it is, zoomed in on the inital version entry in the list of versions:

Notice how there is now a button called “Restore this version”. It is only visible for the owner of the image. Let’s click it:

The image owner confirms the restore and the original version of the image data is rolled back. When we now look again at the sidebar of the image, we see this:

Now, let’s have a final look at the image history screen:

We can now see that the enrichements made by user “Jungle” are undone. We also see on top of the version list that the restore was made by user “fchristant”, the image owner. This completes our scenario.

Why is this so cool?

Only after actually implementing this feature, I realized its tremendous power. First, I thought it was a nice safety net against unwanted edits, a way to make the community aspect of JungleDragon more robust. Then came the realization that this feature brings much more to the table. This feature means that JungleDragon supports all of the following models:

  • No image edits allowed by other users at all (restricted model)
  • Image edits allowed by priviliged users only (karma model)
  • Image edits allowed by all users (Wiki model)

I can support any of the above models by simply setting a few flags in the configuration. In other words:

JungleDragon just became an image Wiki and with the touch of a button, I can control the “Wiki-ness”.

How cool is that?

Finally, thank you Joe for making the suggestion!

In this post I would like to give you an overview of the JungleDragon comment system that I developed from scratch.  I will show you what it looks like, tell you about the features, and I will go into how it’s made.

First things first: What it looks like

The screenshow below shows the comment system in action on an image uploaded to JungleDragon. This should give you an idea of its placement and looks. Click to enlarge.


This screenshot zooms in on the comments:

The features

The screenshot above shows most of the features in action. Still, here’s the overview:

  • Only authenticated users can post comments
  • Comments are validated to be 5 characters in length at least, 2048 characters at most
  • Comments can contain any UNICODE, one of the commenters above is posting in the Bengali language
  • HTML and any other dangerous tags are properly escaped
  • Comments are automatically wrapped to fit the box. The design never breaks down
  • Comments can be nested to an infinite level in the back-end yet is restricted to a fixed treshold in the front-end due to limited screen width. This treshold is a configuration parameter that can be changed easily. No matter the nesting, the total width of all comments will never overflow into the secondary column
  • All nestings can easily be expanded or collapsed using the buttons on top. By default, nested comments are collapsed.
  • Each comment show a rich representation of a user, which includes his/her name, avatar, and status in the JungleDragon repution system. This includes the user’s level (below the avatar) and class (on the right).
  • All dates in the comment system are formatted in a friendly way, for example “posted one hour ago” instead of “2009-12-13″.
  • Users can add a comment to the main thread using the form below the comments
  • Users can reply to a comment (creating a nested comment) using the “Reply” link. This will show an inline comment reply form. This inline reply form will slide down, if you cancel it, it will slide back up. When the inline reply form slides down, it will automatically slide up any other inline reply form that you may have open.
  • Users can vote comments up or down using the vote buttons. You can only vote on comments of another user (not those made by you), and only on comments that you did not yet vote on. Otherwise the vote buttons will be greyed out. The comment vote is done using Ajax, it will not reload the entire page.
  • When a comment is voted down or when its score is below a configurable treshold, it will be minimized, as can be seen in the last comment, made by user “Troll”.
  • When a comment is posted, it does reload the page yet it jumps to the position on the page where your new comment is. Comments currently cannot be edited or deleted, only administrators have this capability.
  • Events associated with the comment system are linked to the JungleDragon reward system, the KarmaEngine. Of particular importance is the event where a user votes on one of your comments. This will give you karma, it will depend on the reputation of that voter how much karma you get.
  • The comments looks and works fine in IE, Firefox, Chrome, Safari and Opera. All styling is controlled by CSS, the markup is pure.
  • Viewing comments and posting main comments works fine with Javascript disabled. Inline replies and expand/collapsing comments will not work, for obvious reasons.

The missing features

I think the comment system described above is fairly rich and robust. Still, it is only fair to also describe what it does not have and why:

  • No edit capability for normal users. Some comment systems allow for comment editing for x seconds after it was posted. I may add this later, but most comment systems do not have this capability and I see few users complaining. Allowing comment edits can also make a conversation confusing when somebody replies while you are editing your original comment.
  • No filtering. There is no way to sort or filter the comments (for example by treshold). It could make for a nice feature for power users, but I think simplicity is better. Less is more.
  • No Ajax posting. This means that when you post a comment, it reloads the page at the position where you made the comment. It is possible to implement a comment post on the page directly using javascript, but I find it adds little value yet a lot of complexity.
  • No pagination. I am assuming there will not be hundreds of comments for a single entry, and that pagination of comments is not needed. I might add it later if I’m proven wrong.

How it’s made

Building a comment system like this from scratch is a ton of work, and not as easy as it looks. It would go too far to explain every little detail of the development process and code. Nevertheless, I will give some high level pointers about the most important areas:

First, storing comments. Since comments can be nested and there is no limit to the deepness of the nesting, in essence we’re storing hierarhcical data. Doing this in a relational database is not hard, all you need to do is creating a foreign key to the primary key of the comment table to indicate a parent relationship. However, querying a table designed like that is highly inefficient. That’s why I’m using a so-called lineage technique. I’ve written a dedicated article about it before.

The lineage technique makes querying a set of comments in the correct reply order easy. The next step is to output those results into markup. This is simply a question of looping through the comments and keeping some counters to track how deep we are in the nesting. Based on that logic, the following is outputted:

  • A top level comment place holder that is the container for all comments
  • Next, per row either a comment container or a comment group container, depending on the nesting deepness and whether there is a change in that deepness
  • Per comment:
    • A comment header, which is used for the user information, such as the avatar and class
    • A comment body container that contains the actual comment text
    • A comment vote block which include the vote score, and the up and down buttons
    • A comment reply link, which reveals the inline reply form when clicked
    • A comment replies link, that indicates the number of replies and a link to expand or collapse them
    • Identification attributes that are used by the Ajax behavior
  • Finally, the main comment form that sits below all comments

Up next is the styling. There is not much to say about this, it’s all normal CSS really. What’s cool is that the markup is very simple and the CSS is very rich. Pretty much everything is controlled in CSS. One important aspect of the CSS is the trick I use to indent a comment below its parent. It works no matter how deep a comment isin the nesting. It looks like this:

.c-nest { float:left; padding-left:3%; display:none; width:97%; }

The c-nest class is in essence a group of comments within the same deepness. In this group there can be other c-nest groups to indicate one level deeper and so on. Using the CSS above, comments are always indented below their parent yet never take more horizontal space than the fixed comments container allows.

Finally comes the interaction, the Javascript. It is all based on jQuery. The following methods are implemented:

  • Comment voting. This will do an Ajax post to my back-end, retrieve the new score, render the new score, and disable the vote panel to prevent a double vote. When a comment is voted down, it will collapse the comment body.
  • Exand / Collapse all comments. This script simply looks through all nestings and sets the visibility to true, thereby showing all replies.
  • Expand / collapse per comment. Does the same as above, but this time only for one comment.
  • Inline reply. This script slides down an inline reply form at the place where the user clicked “Reply”. It will slide it back up when the user hits “Cancel”. The form that appears is not hardcoded markup in jQuery, it will actually get the form markup from the back-end, using Ajax.
  • Toggle. This toggles the visibility of one comment’s body, for example when a comment is below a treshold or voted down.

This completes the JungleDragon comment system overview. It is actually one of my favorite parts of JungleDragon, since it is quite rich, yet still robust and light. If I ever need to develop another comment system from scratch, surely I will lean on what I created here.

I’m curious to hear what you think about it. I’m also willing to answer any questions you may have about its implementation. Keep that feedback coming please.

In the things I shared about JungleDragon, I have talked a lot about karma. Karma is what you earn by positive actions, such as uploading an image or getting good votes on your images or comments. As you grow in karma, you can get promoted to a higher class. Classes are organized along a foodchain, where for example an ant is lower than the lion.

The class of a user is displayed everywhere a user is displayed, acting like a badge of honour. The primary theory behind this is that users are willing to be active just in order to earn and show of this badge. Today, however, I made it even more attractive to get promoted to higher classes. Click the screenshot below to enlarge:


I have tied a number of capabilities to classes:

Upload quota

The higher your class, the higher your upload quota. This system is mostly in place to avoid the situation where a lot of low class users are uploading a lot of images of low value. Any user who uploads good images will not have to worry too much about their quota.

Vote power

The higher your class, the higher your influence in votes on images and comments. This system rewards good users and gives them more power. Votes are of course important for showing things like lists of most popular images.

Vote defense

The higher your class, the better your defense against negative votes. This one only applies to comments, since images cannot be voted down. What happens is that if you have a high class, the impact of negative comment votes is reduced. This protects valuable users from “trolling” low class users.

Moderation power

This ability is very special, only preserved for the top classes. When you have a top class, you are able to edit the metadata of all images, including those not uploaded by you. The idea behind this is that you have proven to be a top community player, and therefore can be trusted to correct the data of others.

What do you think?

I’m quite happy with this idea of extending the use of classes. This way, a class is more than just a pretty picture to show of. It becomes even more attractive for a user to make positive contributions. Another stimulator. The best part about it is that it is not just good for that user alone, it is good for JungleDragon itself, as it offloads the task of moderation largely to the community itself. Jungledragon will not just be about user-generated content, it will be user-moderated too.

Curious to know what you think about this system?

Important news on the JungleDragon front: I will dedicate the entire month of December to fulltime JungleDragon development. I am making a significant investment in this project by taking unpaid leave from the day job. The mission of this “marathon” is simple: making as much progress possible in the allowed time. After six months of dissapointing progress, I’m taking the project by the horns again.

A special thanks to my manager at work, for allowing me to take this leave. And of course a special thanks to my love Henriette, who believes in the project. She put up 12 paintings all across the house to inspire me. JungleDragon paintings that is.

Wish me luck, wisdom and discipline.