diff --git a/README.md b/README.md
index 757e9580b335a30cefe2d8e7becbc3d74de4b1ee..b6014387ad218dd2b9ac28141de0deafebe69a65 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,8 @@ Features
 - Redox support.
 - 256-color mode.
 - Panic-free error handling.
-- Special keys events.
+- Special keys events (modifiers, special keys, etc.).
+- Asynchronous key events.
 
 and much more.
 
diff --git a/examples/async.rs b/examples/async.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8053ee66ef54614bb486ada5f66265c3685651bd
--- /dev/null
+++ b/examples/async.rs
@@ -0,0 +1,35 @@
+extern crate libterm;
+
+use libterm::{TermWrite, IntoRawMode, async_stdin};
+use std::io::{Read, Write, stdout, stdin};
+use std::thread;
+use std::time::Duration;
+
+fn main() {
+    let stdout = stdout();
+    let mut stdout = stdout.lock().into_raw_mode().unwrap();
+    let mut stdin = async_stdin().bytes();
+
+    stdout.clear().unwrap();
+    stdout.goto(0, 0).unwrap();
+
+    loop {
+        stdout.clear_line().unwrap();
+
+        let b = stdin.next();
+        write!(stdout, "\r{:?}    <- This demonstrates the async read input char. Between each update a 100 ms. is waited, simply to demonstrate the async fashion. \n\r", b).unwrap();
+        if let Some(Ok(b'q')) = b {
+            break;
+        }
+
+        stdout.flush().unwrap();
+
+        thread::sleep(Duration::from_millis(50));
+        stdout.write(b"# ").unwrap();
+        stdout.flush().unwrap();
+        thread::sleep(Duration::from_millis(50));
+        stdout.write(b"\r #").unwrap();
+        stdout.goto(0, 0).unwrap();
+        stdout.flush().unwrap();
+    }
+}
diff --git a/examples/simple.rs b/examples/simple.rs
index 61bd7ef770be6afcae6e62e94e37a79cf2d9125b..e1d577c7d4645f8b5dce26783ddb707af1c4714e 100644
--- a/examples/simple.rs
+++ b/examples/simple.rs
@@ -7,6 +7,7 @@ fn main() {
     let stdout = stdout();
     let mut stdout = stdout.lock().into_raw_mode().unwrap();
     let stdin = stdin();
+    let stdin = stdin.lock();
 
     stdout.goto(5, 5).unwrap();
     stdout.clear().unwrap();
diff --git a/src/async.rs b/src/async.rs
new file mode 100644
index 0000000000000000000000000000000000000000..05f424c4f7b9a884b7bd70d4dab6e501096ebfaf
--- /dev/null
+++ b/src/async.rs
@@ -0,0 +1,75 @@
+use std::io::{self, Read};
+use std::sync::mpsc;
+use std::thread;
+
+/// Construct an asynchronous handle to the standard input.
+///
+/// This allows you to read from standard input _without blocking_ the current thread.
+/// Specifically, it works by firing up another thread to handle the event stream, which will then
+/// be buffered in a mpsc queue, which will eventually be read by the current thread.
+///
+/// Note that this will acquire the Mutex lock on the standard input, making all future stdin
+/// construction hang the program.
+pub fn async_stdin() -> AsyncReader {
+    let (send, recv) = mpsc::channel();
+
+    thread::spawn(move || {
+        let stdin = io::stdin();
+        for i in stdin.lock().bytes() {
+            if send.send(i).is_err() {
+                return;
+            }
+        }
+    });
+
+    AsyncReader {
+        recv: recv,
+    }
+}
+
+/// An asynchronous reader.
+pub struct AsyncReader {
+    /// The underlying mpsc receiver.
+    #[doc(hidden)]
+    pub recv: mpsc::Receiver<io::Result<u8>>,
+}
+
+impl Read for AsyncReader {
+    /// Read from the byte stream.
+    ///
+    /// This will never block, but try to drain the event queue until empty. If the total number of
+    /// bytes written is lower than the buffer's length, the event queue is empty or that the event
+    /// stream halted.
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        let mut total = 0;
+
+        loop {
+            match self.recv.try_recv() {
+                Ok(Ok(b)) => {
+                    buf[total] = b;
+                    total += 1;
+
+                    if total == buf.len() {
+                        break;
+                    }
+                },
+                Ok(Err(e)) => return Err(e),
+                Err(_) => break,
+            }
+        }
+
+        Ok(total)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::io::Read;
+
+    #[test]
+    fn test_async_stdin() {
+        let stdin = async_stdin();
+        stdin.bytes().next();
+    }
+}
diff --git a/src/control.rs b/src/control.rs
index 1bcd41d854fba439bd6721dd1fa7f4cd6a33e7b3..57211e53891c9720ecaaae839ee7e1771d8a62b1 100644
--- a/src/control.rs
+++ b/src/control.rs
@@ -1,4 +1,4 @@
-use std::io::{Write, Result as IoResult};
+use std::io::{self, Write};
 use {Color, Style};
 
 /// Extension to the `Write` trait.
@@ -8,39 +8,39 @@ use {Color, Style};
 pub trait TermWrite {
 
     /// Print the CSI (control sequence introducer) followed by a byte string.
-    fn csi(&mut self, b: &[u8]) -> IoResult<usize>;
+    fn csi(&mut self, b: &[u8]) -> io::Result<usize>;
     /// Print OSC (operating system command) followed by a byte string.
-    fn osc(&mut self, b: &[u8]) -> IoResult<usize>;
+    fn osc(&mut self, b: &[u8]) -> io::Result<usize>;
     /// Print OSC (device control string) followed by a byte string.
-    fn dsc(&mut self, b: &[u8]) -> IoResult<usize>;
+    fn dsc(&mut self, b: &[u8]) -> io::Result<usize>;
 
 
     /// Clear the entire screen.
-    fn clear(&mut self) -> IoResult<usize> {
+    fn clear(&mut self) -> io::Result<usize> {
         self.csi(b"2J")
     }
     /// Clear everything _after_ the cursor.
-    fn clear_after(&mut self) -> IoResult<usize> {
+    fn clear_after(&mut self) -> io::Result<usize> {
         self.csi(b"J")
     }
     /// Clear everything _before_ the cursor.
-    fn clear_before(&mut self) -> IoResult<usize> {
+    fn clear_before(&mut self) -> io::Result<usize> {
         self.csi(b"1J")
     }
     /// Clear the current line.
-    fn clear_line(&mut self) -> IoResult<usize> {
+    fn clear_line(&mut self) -> io::Result<usize> {
         self.csi(b"2K")
     }
     /// Clear from the cursor until newline.
-    fn clear_until_newline(&mut self) -> IoResult<usize> {
+    fn clear_until_newline(&mut self) -> io::Result<usize> {
         self.csi(b"K")
     }
     /// Show the cursor.
-    fn show_cursor(&mut self) -> IoResult<usize> {
+    fn show_cursor(&mut self) -> io::Result<usize> {
         self.csi(b"?25h")
     }
     /// Hide the cursor.
-    fn hide_cursor(&mut self) -> IoResult<usize> {
+    fn hide_cursor(&mut self) -> io::Result<usize> {
         self.csi(b"?25l")
     }
 
@@ -51,21 +51,21 @@ pub trait TermWrite {
     /// Reset the rendition mode.
     ///
     /// This will reset both the current style and color.
-    fn reset(&mut self) -> IoResult<usize> {
+    fn reset(&mut self) -> io::Result<usize> {
         self.csi(b"m")
     }
     /// Restore the defaults.
     ///
     /// This will reset color, position, cursor state, and so on. It is recommended that you use
     /// this before you exit your program, to avoid messing up the user's terminal.
-    fn restore(&mut self) -> IoResult<usize> {
+    fn restore(&mut self) -> io::Result<usize> {
         Ok(try!(self.reset()) + try!(self.clear()) + try!(self.goto(0, 0)) + try!(self.show_cursor()))
     }
 
     /// Go to a given position.
     ///
     /// The position is 0-based.
-    fn goto(&mut self, mut x: u16, mut y: u16) -> IoResult<usize> {
+    fn goto(&mut self, mut x: u16, mut y: u16) -> io::Result<usize> {
         x += 1;
         y += 1;
 
@@ -85,7 +85,7 @@ pub trait TermWrite {
         ])
     }
     /// Set graphic rendition.
-    fn rendition(&mut self, r: u8) -> IoResult<usize> {
+    fn rendition(&mut self, r: u8) -> io::Result<usize> {
         self.csi(&[
             b'0' + r / 100,
             b'0' + r / 10 % 10,
@@ -94,7 +94,7 @@ pub trait TermWrite {
         ])
     }
     /// Set foreground color
-    fn color(&mut self, color: Color) -> IoResult<usize> {
+    fn color(&mut self, color: Color) -> io::Result<usize> {
         let ansi = color.to_ansi_val();
         self.csi(&[
             b'3',
@@ -109,7 +109,7 @@ pub trait TermWrite {
         ])
     }
     /// Set background color
-    fn bg_color(&mut self, color: Color) -> IoResult<usize> {
+    fn bg_color(&mut self, color: Color) -> io::Result<usize> {
         let ansi = color.to_ansi_val();
         self.csi(&[
             b'4',
@@ -124,19 +124,19 @@ pub trait TermWrite {
         ])
     }
     /// Set rendition mode (SGR).
-    fn style(&mut self, mode: Style) -> IoResult<usize> {
+    fn style(&mut self, mode: Style) -> io::Result<usize> {
         self.rendition(mode as u8)
     }
 }
 
 impl<W: Write> TermWrite for W {
-    fn csi(&mut self, b: &[u8]) -> IoResult<usize> {
+    fn csi(&mut self, b: &[u8]) -> io::Result<usize> {
         Ok(try!(self.write(b"\x1B[")) + try!(self.write(b)))
     }
-    fn osc(&mut self, b: &[u8]) -> IoResult<usize> {
+    fn osc(&mut self, b: &[u8]) -> io::Result<usize> {
         Ok(try!(self.write(b"\x1B]")) + try!(self.write(b)))
     }
-    fn dsc(&mut self, b: &[u8]) -> IoResult<usize> {
+    fn dsc(&mut self, b: &[u8]) -> io::Result<usize> {
         Ok(try!(self.write(b"\x1BP")) + try!(self.write(b)))
     }
 }
diff --git a/src/input.rs b/src/input.rs
index 5045b6247ad260151c74e55969b5246417b62b9c..0095f503779c17b5588446e15bf26f66871ad922 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -1,11 +1,14 @@
-use std::io::{Read, Write, Error, ErrorKind, Result as IoResult};
-use IntoRawMode;
+use std::io::{self, Read, Write};
+use std::thread;
+use std::sync::mpsc;
+
+use {IntoRawMode, AsyncReader};
 
 #[cfg(feature = "nightly")]
 use std::io::{Chars, CharsError};
 
 /// A key.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+#[derive(Debug)]
 pub enum Key {
     /// Backspace.
     Backspace,
@@ -27,9 +30,8 @@ pub enum Key {
     Ctrl(char),
     /// Invalid character code.
     Invalid,
-    // TODO handle errors better?
     /// IO error.
-    Error,
+    Error(io::Error),
     /// Null byte.
     Null,
 
@@ -69,7 +71,7 @@ impl<I: Iterator<Item = Result<char, CharsError>>> Iterator for Keys<I> {
             None => None,
             Some('\0') => Some(Key::Null),
             Some(Ok(c)) => Some(Key::Char(c)),
-            Some(Err(_)) => Some(Key::Error),
+            Some(Err(e)) => Some(Key::Error(e)),
         }
     }
 }
@@ -84,7 +86,12 @@ pub trait TermRead {
     ///
     /// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
     /// complete the password input.
-    fn read_passwd<W: Write>(&mut self, writer: &mut W) -> IoResult<Option<String>>;
+    fn read_passwd<W: Write>(&mut self, writer: &mut W) -> io::Result<Option<String>>;
+
+    /// Turn the reader into a asynchronous reader.
+    ///
+    /// This will spawn up another thread listening for event, buffering them in a mpsc queue.
+    fn into_async(self) -> AsyncReader where Self: Send;
 }
 
 impl<R: Read> TermRead for R {
@@ -95,7 +102,7 @@ impl<R: Read> TermRead for R {
         }
     }
 
-    fn read_passwd<W: Write>(&mut self, writer: &mut W) -> IoResult<Option<String>> {
+    fn read_passwd<W: Write>(&mut self, writer: &mut W) -> io::Result<Option<String>> {
         let _raw = try!(writer.into_raw_mode());
         let mut passbuf = Vec::with_capacity(30);
 
@@ -108,10 +115,32 @@ impl<R: Read> TermRead for R {
             }
         }
 
-        let passwd = try!(String::from_utf8(passbuf).map_err(|e| Error::new(ErrorKind::InvalidData, e)));
+        let passwd = try!(String::from_utf8(passbuf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)));
 
         Ok(Some(passwd))
     }
+
+    fn into_async(self) -> AsyncReader where R: Send + 'static {
+        let (send, recv) = mpsc::channel();
+
+        thread::spawn(move || {
+            let mut reader = self;
+            loop {
+                let mut buf = [0];
+                if send.send(if let Err(k) = reader.read(&mut buf) {
+                    Err(k)
+                } else {
+                    Ok(buf[0])
+                }).is_err() {
+                    return;
+                };
+            }
+        });
+
+        AsyncReader {
+            recv: recv,
+        }
+    }
 }
 
 #[cfg(test)]
diff --git a/src/lib.rs b/src/lib.rs
index 9f6f3016a957cf8d9884424408120dc39caf2fd7..c459fe7f88cb099389269c959ba65de053f41ff0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,9 @@ mod termios;
 mod control;
 pub use control::TermWrite;
 
+mod async;
+pub use async::{AsyncReader, async_stdin};
+
 mod input;
 pub use input::{TermRead, Key};
 #[cfg(feature = "nightly")]
diff --git a/src/size.rs b/src/size.rs
index 671edfb1107a278ae2e076a31567d13ab670842d..f7e9af93c0a2d9191810bd0b72a8595cb4ad53d6 100644
--- a/src/size.rs
+++ b/src/size.rs
@@ -1,4 +1,4 @@
-use std::io::{Error, ErrorKind, Result as IoResult};
+use std::io;
 
 #[cfg(not(target_os = "redox"))]
 use libc::c_ushort;
@@ -27,7 +27,7 @@ fn tiocgwinsz() -> u32 {
 
 /// Get the size of the terminal.
 #[cfg(not(target_os = "redox"))]
-pub fn terminal_size() -> IoResult<(usize, usize)> {
+pub fn terminal_size() -> io::Result<(usize, usize)> {
     use libc::ioctl;
     use libc::STDOUT_FILENO;
 
@@ -38,22 +38,22 @@ pub fn terminal_size() -> IoResult<(usize, usize)> {
         if ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _) == 0 {
             Ok((size.col as usize, size.row as usize))
         } else {
-            Err(Error::new(ErrorKind::Other, "Unable to get the terminal size."))
+            Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
         }
     }
 }
 
 /// Get the size of the terminal.
 #[cfg(target_os = "redox")]
-pub fn terminal_size() -> IoResult<(usize, usize), TerminalError> {
-    fn get_int(s: &'static str) -> IoResult<usize> {
-        use std::env::{VarError, var};
+pub fn terminal_size() -> io::Result<(usize, usize)> {
+    fn get_int(s: &'static str) -> io::Result<usize> {
+        use std::env;
 
-        var(s).map_err(|e| match e {
-            VarError::NotPresent => Error::new(ErrorKind::NotFound, e),
-            VarError::NotUnicode(u) => Error::new(ErrorKind::InvalidData, u),
+        env::var(s).map_err(|e| match e {
+            env::VarError::NotPresent => io::Error::new(io::ErrorKind::NotFound, e),
+            env::VarError::NotUnicode(u) => io::Error::new(io::ErrorKind::InvalidData, u),
         }).and_then(|x| {
-            x.parse().map_err(|e| Error::new(ErrorKind::InvalidData, e))
+            x.parse().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
         })
     }