diff --git a/ajax_comments.js b/ajax_comments.js
index 35e21e0..a62ca71 100644
--- a/ajax_comments.js
+++ b/ajax_comments.js
@@ -38,7 +38,7 @@ Drupal.behaviors.ajaxComments = {
         click: function(e) {
           commentNumber = $(this).attr("id").split('-');
           // Reshow the form.
-          $('[about="/comment/' + commentNumber[1] + '#comment-' + commentNumber[1] + '"]').next().show();
+          $('#comment-wrapper-' + commentNumber[1] + ' .comment-form').show();
 
           // Don't let people reply over and over.
           $(this).hide();
diff --git a/ajax_comments.module b/ajax_comments.module
index db6605e..d8e110b 100644
--- a/ajax_comments.module
+++ b/ajax_comments.module
@@ -4,6 +4,9 @@
  * @file
  * AJAX comments module file.
  */
+define('AJAX_COMMENTS_COMMENT_CREATED', 1);
+define('AJAX_COMMENTS_COMMENT_UPDATED', 2);
+define('AJAX_COMMENTS_COMMENT_DELETED', 3);
 
 /**
  * Implements hook_init().
@@ -86,7 +89,7 @@ function ajax_comments_views_api() {
  * Implements hook_views_data().
  */
 function ajax_comments_views_data() {
-  
+
   $data['node']['list_comments'] = array(
     'title' => t('List of comments'),
     'help' => t("Display the node's list of comments."),
@@ -94,7 +97,7 @@ function ajax_comments_views_data() {
       'handler' => 'ajax_comments_handler_field_list_comments',
     ),
   );
-  
+
   $data['node']['ajax_comment'] = array(
     'title' => t('AJAX Add Comment'),
     'help' => t('Adds an inline AJAX comment form.'),
@@ -193,11 +196,11 @@ function ajax_comments_preview_js($form, &$form_state) {
   if (form_get_errors()) {
     return $form;
   }
-  
+
   $comment = comment_form_submit_build_comment($form, $form_state);
   $comment_pre_render = comment_preview($comment);
   $comment_output = drupal_render($comment_pre_render['comment_preview']);
-  
+
   $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text', array('type' => 'preview', 'comment' => $comment)) : '';
 
   // This is a reply.
@@ -219,7 +222,7 @@ function ajax_comments_preview_js($form, &$form_state) {
   }
   // Or is this a brand new comment.
   else {
-    $commands[] = ajax_command_append('#comment-wrapper', $notify_text . $comment_output);
+    $commands[] = ajax_command_append('.comment-wrapper', $notify_text . $comment_output);
     $new_form_state = array();
     $new_form_state['build_info']['args'][] = (object) array('nid' => $node->nid);
     $new_form_state['input'] = $form_state['input'];
@@ -249,83 +252,174 @@ function ajax_comments_edit_cancel($node, $comment) {
 }
 
 /**
+ * Derived from comment_get_thread(), removed pager support
+ * (when updating comments via AJAX, we don't care)
+ * and added the ability to only get comments for a certain
+ * thread when comments are threaded.
+ */
+function ajax_comments_get_thread($comment, $mode) {
+  $query = db_select('comment', 'c');
+  $query->addField('c', 'cid');
+  $query->condition('c.nid', $comment->nid);
+
+  if (!user_access('administer comments')) {
+    $query->condition('c.status', COMMENT_PUBLISHED);
+  }
+  if ($mode === COMMENT_MODE_FLAT) {
+    $query->orderBy('c.cid', 'ASC');
+  }
+  else {
+    // See comment above. Analysis reveals that this doesn't cost too
+    // much. It scales much much better than having the whole comment
+    // structure.
+    $query->addExpression('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'torder');
+    $query->orderBy('torder', 'ASC');
+
+    // only get comments for this comment's thread
+    if (isset($comment->thread)) {
+      $query->condition('c.thread', db_like(substr($comment->thread, 0, -1)) . '%', 'LIKE');
+    }
+  }
+
+  $cids = $query->execute()->fetchCol();
+
+  return $cids;
+}
+
+/**
  * Builds the comment.
  */
+function ajax_comments_create_ajax_response($action, $node, $comment) {
+  $commands = array();
+
+  switch ($action) {
+    case AJAX_COMMENTS_COMMENT_CREATED:
+      $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text') : '';
+      $comment_build = comment_view($comment, $node);
+      // Brand new comment
+      if (!$comment->pid) {
+        // Append comment to root comment wrapper
+        $comment_output = drupal_render($comment_build);
+        // $commands[] = ajax_command_after('#comment-wrapper-nid-' . $node->nid . " .ajax-comment-wrapper:last", $notify_text . $comment_output);
+        if ($node->comment_count > variable_get('comment_default_per_page_'. $node->type, 50)) {
+          $commands[] = ajax_command_before('#comment-wrapper-nid-' . $node->nid . ' .item-list', $notify_text . $comment_output);
+        }
+        elseif ($node->comment_count == 0) {
+          $commands[] = ajax_command_append('#comment-wrapper-nid-' . $node->nid, $notify_text . $comment_output);
+        }
+        else {
+          $commands[] = ajax_command_after('#comment-wrapper-nid-' . $node->nid . " .ajax-comment-wrapper:last", $notify_text . $comment_output);
+        }
+      }
+      // Replying to comment
+      else {
+        // Append comment to parent wrapper
+        $comment_output = drupal_render($comment_build);
+        $commands[] = ajax_command_append('#comment-wrapper-' . $comment->pid, $notify_text . $comment_output);
+      }
+      break;
+
+    case AJAX_COMMENTS_COMMENT_UPDATED:
+      $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text') : '';
+      $mode = variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED);
+      if ($cids = ajax_comments_get_thread($comment, $mode)) {
+        $comments = comment_load_multiple($cids);
+        comment_prepare_thread($comments);
+        $comment_build = comment_view_multiple($comments, $node);
+        $comment_output = drupal_render($comment_build);
+        $commands[] = ajax_command_replace('#comment-wrapper-' . $comment->cid, $notify_text . $comment_output);
+      }
+    break;
+
+    case AJAX_COMMENTS_COMMENT_DELETED:
+      $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text', array('type' => 'delete')) : '';
+      if ($notify_text) {
+        $commands[] = ajax_command_replace('#comment-wrapper-' . $comment->cid, $notify_text);
+      }
+      else {
+        $commands[] = ajax_command_remove('#comment-wrapper-' . $comment->cid);
+      }
+    break;
+  }
+  return $commands;
+}
+
 function ajax_comments_submit_js($form, &$form_state) {
   // Return the actual form if it contains errors.
   if (form_get_errors()) {
     return $form;
   }
 
+  $comment = $form_state['comment'];
+  $node = $form['#node'];
+
+//  // If this is being submitted via the views ajax add comment field.
+//  elseif ($form_state['comment']->views_ajax_submission) {
+//    $message = t('Your comment has been posted.');
+//    $commands[] = ajax_command_replace('#' . $form['#id'], $message);
+//  }
+//  // Or is this a brand new comment?
+//  else {
+//    // Append comment to root comment wrapper.
+//    $comment_output = drupal_render($comment_build);
+//
+//    if ($node->comment_count == 0) {
+//      $commands[] = ajax_command_append('#comment-wrapper-nid-' . $node->nid, $notify_text . $comment_output);
+//    }
+//    else {
+//      $commands[] = ajax_command_after('#comment-wrapper-nid-' . $node->nid . " .ajax-comment-wrapper:last", $notify_text . $comment_output);
+//    }
+
   // This is to remove the "Your comment has been posted" status message that
   // will appear upon refresh. This seems dirty but it means we don't have to
   // rewrite the whole comment_form_submit(). Please chime in if you think this
   // is dumb.
-  ajax_comments_remove_status();
-
-  $comment = $form_state['comment'];
-  $node = $form['#node'];
-  $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text', array('comment' => $comment)) : '';
-
-  $comment_build = comment_view($comment, $node);
+  ajax_comments_remove_status($_SESSION);
 
   if (variable_get('comment_preview_' . $node->type)) {
     $commands[] = ajax_command_remove('.comment-preview');
     $commands[] = ajax_command_remove('.messages.ajax-comments.warning');
   }
 
-  // Don't display as a preview as this is being submitted.
-  unset($comment_build['comment_body']['#object']->in_preview);
-
-  // Are we editing a comment.
-  if (isset($form['cid']['#value'])) {
-    // Remove wrapper because the form we replace exists inside the wrapper.
-    unset($comment_build['#prefix']);
-    unset($comment_build['#suffix']);
-
-    // Trim surrounding whitespace so ajax.js doesn't wrap us in a new div.
-    $comment_output = trim(drupal_render($comment_build));
-    $commands[] = ajax_command_replace('#' . $form['#id'], $comment_output);
-  }
-  // Or are we replying to another comment.
-  elseif (isset($form_state['values']['pid'])) {
-    // Append comment to parent wrapper.
-    $comment_output = drupal_render($comment_build);
-    $commands[] = ajax_command_append('#comment-wrapper-' . $comment->pid, $notify_text . $comment_output);
-    // Delete the form.
-    $commands[] = ajax_command_invoke('#' . $form['#id'], 'remove');
-  }
-  // If this is being submitted via the views ajax add comment field.
-  elseif ($form_state['comment']->views_ajax_submission) {
-    $message = t('Your comment has been posted.');
-    $commands[] = ajax_command_replace('#' . $form['#id'], $message);
-  }
-  // Or is this a brand new comment?
-  else {
-    // Append comment to root comment wrapper.
-    $comment_output = drupal_render($comment_build);
-
-    if ($node->comment_count == 0) {
-      $commands[] = ajax_command_append('#comment-wrapper-nid-' . $node->nid, $notify_text . $comment_output);
-    }
-    else {
-      $commands[] = ajax_command_after('#comment-wrapper-nid-' . $node->nid . " .ajax-comment-wrapper:last", $notify_text . $comment_output);
-    }
-
-    // If we have a default form, update it with a new one.
-    if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW) {
-      $node = $form['#node'];
-
-      $new_form_state = array();
-      $new_form_state['build_info']['args'][] = (object) array('nid' => $node->nid);
-      // Don't pull from cache.
-      $new_form_state['input'] = array();
-      $new_form_build = drupal_build_form($form['#form_id'], $new_form_state);
-      $new_form_output = drupal_render($new_form_build);
-
-      $commands[] = ajax_command_replace('#' . $form['#id'], $new_form_output);
+  // Figure out what action we're performing
+  $action = isset($form['cid']['#value']) ? AJAX_COMMENTS_COMMENT_UPDATED : AJAX_COMMENTS_COMMENT_CREATED;
+
+  // Generate AJAX commands to actually update the comment content
+  // TODO find a better way of stopping double posts for comment author
+  if (!module_exists('ajax_comments_nodejs')) {
+    $commands = ajax_comments_create_ajax_response($action, $node, $comment);
+  }
+
+  // Now, remove/replace comment forms
+  if ($action == AJAX_COMMENTS_COMMENT_CREATED) {
+    if (!$comment->pid) {
+      // If we have a default form, update it with a new one
+      if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW) {
+        $node = $form['#node'];
+
+        $new_form_state = array();
+        $new_form_state['build_info']['args'][] = (object) array('nid' => $node->nid);
+        $new_form_state['input'] = array(); // don't pull from cache
+        $new_form_build = drupal_build_form($form['#form_id'], $new_form_state);
+        $new_form_output = drupal_render($new_form_build);
+
+        // Remove the comment form from its current location and place at the bottom
+        // underneath the recently added comment.
+        // Also move the pager if it exists
+        // TODO Not happy with this, let us instead just erase all the form fields.
+        // $commands[] = ajax_command_remove('.comment-wrapper .pager');
+        // $commands[] = ajax_command_append('.comment-wrapper', drupal_render($pager['#theme'] = 'pager'));
+        $commands[] = ajax_command_remove('#' . $form['#id']);
+        $commands[] = ajax_command_remove('h2.comment-form');
+        $commands[] = ajax_command_append('.comment-wrapper', '<h2 class="title comment-form">' . t('Add new comment') . '</h2>');
+        $commands[] = ajax_command_append('.comment-wrapper', $new_form_output);
+      }
+      // Otherwise, delete it
+      else {
+        $commands[] = ajax_command_remove('#' . $form['#id']);
+      }
     }
-    // Otherwise, delete it.
+    // Reply to a comment
     else {
       $commands[] = ajax_command_remove('#' . $form['#id']);
     }
@@ -342,14 +436,7 @@ function ajax_comments_delete_js($form, &$form_state) {
 
   ajax_comments_remove_status();
 
-  $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text', array('type' => 'delete', 'comment' => $comment)) : '';
-
-  if ($notify_text) {
-    $commands[] = ajax_command_replace('#comment-wrapper-' . $comment->cid, $notify_text);
-  }
-  else {
-    $commands[] = ajax_command_remove('#comment-wrapper-' . $comment->cid);
-  }
+  $commands = ajax_comments_create_ajax_response(AJAX_COMMENTS_COMMENT_DELETED, NULL, $comment);
 
   return array('#type' => 'ajax', '#commands' => $commands);
 }
diff --git a/ajax_comments_nodejs/ajax_comments_nodejs.info b/ajax_comments_nodejs/ajax_comments_nodejs.info
index cbda69c..f11c775 100644
--- a/ajax_comments_nodejs/ajax_comments_nodejs.info
+++ b/ajax_comments_nodejs/ajax_comments_nodejs.info
@@ -1,5 +1,6 @@
 name = AJAX Comments Nodejs Integration
-description = Integrates Nodejs with Ajax Comments 
+description = Integrates Nodejs with Ajax Comments
 core = 7.x
 dependencies[] = comment
-dependencies[] = nodejs
\ No newline at end of file
+dependencies[] = nodejs
+dependencies[] = ajax_comments
\ No newline at end of file
diff --git a/ajax_comments_nodejs/ajax_comments_nodejs.js b/ajax_comments_nodejs/ajax_comments_nodejs.js
new file mode 100644
index 0000000..2ed3925
--- /dev/null
+++ b/ajax_comments_nodejs/ajax_comments_nodejs.js
@@ -0,0 +1,40 @@
+(function ($) {
+
+  function runCommands(commands) {
+    var element_settings = {event: 'fake_events', url: ''};
+    var element = $('');
+    var ajax_comments = new Drupal.ajax('ajax_comments', element, element_settings);
+
+    for (var i in commands) {
+      var command = commands[i],
+          command_name = command['command'];
+
+      if (command_name && ajax_comments.commands[command_name]) {
+        ajax_comments.commands[command_name](ajax_comments, command, 200);
+      }
+    }
+  }
+
+  Drupal.Nodejs.callbacks.ajaxCommentsInsert = {
+    callback: function (message) {
+      $.get('/ajax_comments_nodejs/insert/' + message.cid, function (data) {
+        runCommands(data);
+      });
+    }
+  };
+
+  Drupal.Nodejs.callbacks.ajaxCommentsUpdate = {
+    callback: function (message) {
+      $.get('/ajax_comments_nodejs/update/' + message.cid, function (data) {
+        runCommands(data);
+      });
+    }
+  };
+
+  Drupal.Nodejs.callbacks.ajaxCommentsDelete = {
+    callback: function (message) {
+      runCommands(message.commands);
+    }
+  };
+
+})(jQuery);
diff --git a/ajax_comments_nodejs/ajax_comments_nodejs.module b/ajax_comments_nodejs/ajax_comments_nodejs.module
index 7f83b7b..9215b52 100644
--- a/ajax_comments_nodejs/ajax_comments_nodejs.module
+++ b/ajax_comments_nodejs/ajax_comments_nodejs.module
@@ -1,6 +1,109 @@
 <?php
 
 /**
- * @file
- *  Right now this is a stub. Please use this to commit patches.
+ * Implementation of hook_menu()
  */
+function ajax_comments_nodejs_menu() {
+  $items['ajax_comments_nodejs/%/%'] = array(
+    'page callback' => 'ajax_comments_nodejs_view_comment',
+    'page arguments' => array(1, 2),
+    'access arguments' => array('access comments'),
+    'delivery callback' => 'ajax_deliver',
+    'type' => MENU_CALLBACK,
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_nodejs_handlers_info().
+ */
+function ajax_comments_nodejs_nodejs_handlers_info() {
+  return array(
+    drupal_get_path('module', 'ajax_comments_nodejs') . '/ajax_comments_nodejs.js',
+  );
+}
+
+/**
+ * Implements hook_page_alter().
+ */
+function ajax_comments_nodejs_page_alter(&$page) {
+  $node = menu_get_object();
+
+  $allowed_types = variable_get('ajax_comments_node_types', array());
+  if ($node && in_array($node->type, $allowed_types)) {
+    nodejs_send_content_channel_token('ajax_comments_' . $node->nid);
+  }
+}
+
+/**
+ * Implements hook_form_comment_form_alter().
+ * This is not using hook_comment_insert/hook_comment_update
+ * to avoid running before the comment_save transaction is complete.
+ */
+function ajax_comments_nodejs_form_comment_form_alter(&$form, &$form_state) {
+  if (!empty($form_state['comment']->cid)) {
+    $form_state['ajax_comments'] = 'update';
+  }
+  else {
+    $form_state['ajax_comments'] = 'insert';
+  }
+  $form['actions']['submit']['#submit'][] = 'ajax_comments_nodejs_comment_save';
+}
+
+function ajax_comments_nodejs_comment_save($form, &$form_state) {
+  $comment = comment_form_submit_build_comment($form, $form_state);
+  $message = (object) array(
+    'channel' => 'ajax_comments_' . $comment->nid,
+    'broadcast' => FALSE,
+    'cid' => $comment->cid,
+    'callback' => $form_state['ajax_comments'] == 'update' ? 'ajaxCommentsUpdate' : 'ajaxCommentsInsert',
+  );
+
+  nodejs_send_content_channel_message($message);
+}
+
+/**
+ * Implements hook_comment_delete().
+ */
+function ajax_comments_nodejs_comment_delete($comment) {
+  if (!($node = node_load($comment->nid))) {
+    return MENU_NOT_FOUND;
+  }
+
+  $commands = ajax_comments_create_ajax_response(AJAX_COMMENTS_COMMENT_DELETED, $node, $comment);
+
+  $message = (object) array(
+    'channel' => 'ajax_comments_' . $comment->nid,
+    'broadcast' => FALSE,
+    'commands' => $commands,
+    'callback' => 'ajaxCommentsDelete',
+  );
+
+  nodejs_send_content_channel_message($message);
+}
+
+/**
+ *  Callback for comment request from client.
+ */
+function ajax_comments_nodejs_view_comment($action, $cid) {
+  if (!($comment = comment_load($cid))) {
+    return MENU_NOT_FOUND;
+  }
+
+  if (!($node = node_load($comment->nid))) {
+    return MENU_NOT_FOUND;
+  }
+
+  if ($action == 'insert') {
+    $commands = ajax_comments_create_ajax_response(AJAX_COMMENTS_COMMENT_CREATED, $node, $comment);
+  }
+  elseif ($action == 'update') {
+    $commands = ajax_comments_create_ajax_response(AJAX_COMMENTS_COMMENT_UPDATED, $node, $comment);
+  }
+  else {
+    return MENU_NOT_FOUND;
+  }
+
+  return array('#type' => 'ajax', '#commands' => $commands);
+}
