diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 091adec106fe868933af2a579574cf7e67cc34d0..47f2cdf18451a678a346e12b1b06e36b878503b5 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -154,6 +154,46 @@ e.g. \fBfzf --expect=ctrl-v,ctrl-t,alt-s,f1,f2,~,@\fR
 Key to toggle sort (\fIctrl-[a-z]\fR, \fIalt-[a-z]\fR, \fIf[1-4]\fR,
 or any single character)
 .TP
+.BI "--bind=" "KEYBINDS"
+Comma-separated list of custom key bindings. Each key binding expression
+follows the following format: \fBKEY:ACTION\fR
+.RS
+e.g. \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
+.RE
+
+.RS
+.B KEY:
+    \fIctrl-[a-z]\fR, \fIalt-[a-z]\fR, \fIf[1-4]\fR, or any single character
+.RE
+
+.RS
+.B ACTION:
+    abort
+    accept
+    backward-char
+    backward-delete-char
+    backward-kill-word
+    backward-word
+    beginning-of-line
+    clear-screen
+    delete-char
+    down
+    end-of-line
+    forward-char
+    forward-word
+    kill-line
+    kill-word
+    page-down
+    page-up
+    toggle-down
+    toggle-sort
+    toggle-up
+    unix-line-discard
+    unix-word-rubout
+    up
+    yank
+.RE
+.TP
 .B "--sync"
 Synchronous search for multi-staged filtering. If specified, fzf will launch
 ncurses finder only after the input stream is complete.
diff --git a/src/curses/curses.go b/src/curses/curses.go
index f3263615fb79d7cc1398f62b34c7b3bedcd9819b..44fab4fb4f6e9d425ed3cc6d26090901f95d1571 100644
--- a/src/curses/curses.go
+++ b/src/curses/curses.go
@@ -61,6 +61,16 @@ const (
 	PgUp
 	PgDn
 
+	Up
+	Down
+	Left
+	Right
+	Home
+	End
+
+	SLeft
+	SRight
+
 	F1
 	F2
 	F3
@@ -356,19 +366,19 @@ func escSequence(sz *int) Event {
 		*sz = 3
 		switch _buf[2] {
 		case 68:
-			return Event{CtrlB, 0, nil}
+			return Event{Left, 0, nil}
 		case 67:
-			return Event{CtrlF, 0, nil}
+			return Event{Right, 0, nil}
 		case 66:
-			return Event{CtrlJ, 0, nil}
+			return Event{Down, 0, nil}
 		case 65:
-			return Event{CtrlK, 0, nil}
+			return Event{Up, 0, nil}
 		case 90:
 			return Event{BTab, 0, nil}
 		case 72:
-			return Event{CtrlA, 0, nil}
+			return Event{Home, 0, nil}
 		case 70:
-			return Event{CtrlE, 0, nil}
+			return Event{End, 0, nil}
 		case 77:
 			return mouseSequence(sz)
 		case 80:
@@ -390,7 +400,7 @@ func escSequence(sz *int) Event {
 			case 51:
 				return Event{Del, 0, nil}
 			case 52:
-				return Event{CtrlE, 0, nil}
+				return Event{End, 0, nil}
 			case 53:
 				return Event{PgUp, 0, nil}
 			case 54:
@@ -398,7 +408,7 @@ func escSequence(sz *int) Event {
 			case 49:
 				switch _buf[3] {
 				case 126:
-					return Event{CtrlA, 0, nil}
+					return Event{Home, 0, nil}
 				case 59:
 					if len(_buf) != 6 {
 						return Event{Invalid, 0, nil}
@@ -408,16 +418,16 @@ func escSequence(sz *int) Event {
 					case 50:
 						switch _buf[5] {
 						case 68:
-							return Event{CtrlA, 0, nil}
+							return Event{Home, 0, nil}
 						case 67:
-							return Event{CtrlE, 0, nil}
+							return Event{End, 0, nil}
 						}
 					case 53:
 						switch _buf[5] {
 						case 68:
-							return Event{AltB, 0, nil}
+							return Event{SLeft, 0, nil}
 						case 67:
-							return Event{AltF, 0, nil}
+							return Event{SRight, 0, nil}
 						}
 					} // _buf[4]
 				} // _buf[3]
diff --git a/src/options.go b/src/options.go
index 723c26114cca66a4312c48aafd967d8cee11ca01..abae6f37a6aa53b28b0f2c7e1323be22f06b2074 100644
--- a/src/options.go
+++ b/src/options.go
@@ -51,6 +51,7 @@ const usage = `usage: fzf [options]
         --print-query     Print query as the first line
         --expect=KEYS     Comma-separated list of keys to complete fzf
         --toggle-sort=KEY Key to toggle sort
+        --bind=KEYBINDS   Custom key bindings. Refer to the man page.
         --sync            Synchronous search for multi-staged filtering
 
   Environment variables
@@ -112,8 +113,9 @@ type Options struct {
 	Select1    bool
 	Exit0      bool
 	Filter     *string
-	ToggleSort int
+	ToggleSort bool
 	Expect     []int
+	Keymap     map[int]actionType
 	PrintQuery bool
 	Sync       bool
 	Version    bool
@@ -149,8 +151,9 @@ func defaultOptions() *Options {
 		Select1:    false,
 		Exit0:      false,
 		Filter:     nil,
-		ToggleSort: 0,
+		ToggleSort: false,
 		Expect:     []int{},
+		Keymap:     defaultKeymap(),
 		PrintQuery: false,
 		Sync:       false,
 		Version:    false}
@@ -290,12 +293,82 @@ func parseTheme(str string) *curses.ColorTheme {
 	return nil
 }
 
-func checkToggleSort(str string) int {
+func parseKeymap(keymap map[int]actionType, toggleSort bool, str string) (map[int]actionType, bool) {
+	for _, pairStr := range strings.Split(str, ",") {
+		pair := strings.Split(pairStr, ":")
+		if len(pair) != 2 {
+			errorExit("invalid key binding: " + pairStr)
+		}
+		keys := parseKeyChords(pair[0], "key name required")
+		if len(keys) != 1 {
+			errorExit("invalid key binding: " + pairStr)
+		}
+		key := keys[0]
+		act := strings.ToLower(pair[1])
+		switch strings.ToLower(pair[1]) {
+		case "beginning-of-line":
+			keymap[key] = actBeginningOfLine
+		case "abort":
+			keymap[key] = actAbort
+		case "accept":
+			keymap[key] = actAccept
+		case "backward-char":
+			keymap[key] = actBackwardChar
+		case "backward-delete-char":
+			keymap[key] = actBackwardDeleteChar
+		case "backward-word":
+			keymap[key] = actBackwardWord
+		case "clear-screen":
+			keymap[key] = actClearScreen
+		case "delete-char":
+			keymap[key] = actDeleteChar
+		case "end-of-line":
+			keymap[key] = actEndOfLine
+		case "forward-char":
+			keymap[key] = actForwardChar
+		case "forward-word":
+			keymap[key] = actForwardWord
+		case "kill-line":
+			keymap[key] = actKillLine
+		case "kill-word":
+			keymap[key] = actKillWord
+		case "unix-line-discard", "line-discard":
+			keymap[key] = actUnixLineDiscard
+		case "unix-word-rubout", "word-rubout":
+			keymap[key] = actUnixWordRubout
+		case "yank":
+			keymap[key] = actYank
+		case "backward-kill-word":
+			keymap[key] = actBackwardKillWord
+		case "toggle-down":
+			keymap[key] = actToggleDown
+		case "toggle-up":
+			keymap[key] = actToggleUp
+		case "down":
+			keymap[key] = actDown
+		case "up":
+			keymap[key] = actUp
+		case "page-up":
+			keymap[key] = actPageUp
+		case "page-down":
+			keymap[key] = actPageDown
+		case "toggle-sort":
+			keymap[key] = actToggleSort
+			toggleSort = true
+		default:
+			errorExit("unknown action: " + act)
+		}
+	}
+	return keymap, toggleSort
+}
+
+func checkToggleSort(keymap map[int]actionType, str string) map[int]actionType {
 	keys := parseKeyChords(str, "key name required")
 	if len(keys) != 1 {
 		errorExit("multiple keys specified")
 	}
-	return keys[0]
+	keymap[keys[0]] = actToggleSort
+	return keymap
 }
 
 func parseOptions(opts *Options, allArgs []string) {
@@ -319,10 +392,13 @@ func parseOptions(opts *Options, allArgs []string) {
 			opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required")
 		case "--tiebreak":
 			opts.Tiebreak = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
+		case "--bind":
+			opts.Keymap, opts.ToggleSort = parseKeymap(opts.Keymap, opts.ToggleSort, nextString(allArgs, &i, "bind expression required"))
 		case "--color":
 			opts.Theme = parseTheme(nextString(allArgs, &i, "color scheme name required"))
 		case "--toggle-sort":
-			opts.ToggleSort = checkToggleSort(nextString(allArgs, &i, "key name required"))
+			opts.Keymap = checkToggleSort(opts.Keymap, nextString(allArgs, &i, "key name required"))
+			opts.ToggleSort = true
 		case "-d", "--delimiter":
 			opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required"))
 		case "-n", "--nth":
@@ -409,13 +485,16 @@ func parseOptions(opts *Options, allArgs []string) {
 			} else if match, _ := optString(arg, "-s|--sort="); match {
 				opts.Sort = 1 // Don't care
 			} else if match, value := optString(arg, "--toggle-sort="); match {
-				opts.ToggleSort = checkToggleSort(value)
+				opts.Keymap = checkToggleSort(opts.Keymap, value)
+				opts.ToggleSort = true
 			} else if match, value := optString(arg, "--expect="); match {
 				opts.Expect = parseKeyChords(value, "key names required")
 			} else if match, value := optString(arg, "--tiebreak="); match {
 				opts.Tiebreak = parseTiebreak(value)
 			} else if match, value := optString(arg, "--color="); match {
 				opts.Theme = parseTheme(value)
+			} else if match, value := optString(arg, "--bind="); match {
+				opts.Keymap, opts.ToggleSort = parseKeymap(opts.Keymap, opts.ToggleSort, value)
 			} else {
 				errorExit("unknown option: " + arg)
 			}
diff --git a/src/options_test.go b/src/options_test.go
index 36959da462dd77dc3d562dc7dc7464b5ff99ecf5..ad9a6fb5f0875d48fa1232313a025f35a568e823 100644
--- a/src/options_test.go
+++ b/src/options_test.go
@@ -129,3 +129,29 @@ func TestParseKeysWithComma(t *testing.T) {
 	check(len(keys), 1)
 	check(keys[0], curses.AltZ+',')
 }
+
+func TestBind(t *testing.T) {
+	check := func(action actionType, expected actionType) {
+		if action != expected {
+			t.Errorf("%d != %d", action, expected)
+		}
+	}
+	keymap := defaultKeymap()
+	check(actBeginningOfLine, keymap[curses.CtrlA])
+	keymap, toggleSort :=
+		parseKeymap(keymap, false,
+			"ctrl-a:kill-line,ctrl-b:toggle-sort,c:page-up,alt-z:page-down")
+	if !toggleSort {
+		t.Errorf("toggleSort not set")
+	}
+	check(actKillLine, keymap[curses.CtrlA])
+	check(actToggleSort, keymap[curses.CtrlB])
+	check(actPageUp, keymap[curses.AltZ+'c'])
+	check(actPageDown, keymap[curses.AltZ])
+
+	keymap, toggleSort = parseKeymap(keymap, false, "f1:abort")
+	if toggleSort {
+		t.Errorf("toggleSort set")
+	}
+	check(actAbort, keymap[curses.F1])
+}
diff --git a/src/terminal.go b/src/terminal.go
index c38d7146a0cdef25b573f3f65a4e8218b415c965..1a78fed9ff280b961d4e3a6a60d0a147c7d3dff0 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -31,8 +31,9 @@ type Terminal struct {
 	input      []rune
 	multi      bool
 	sort       bool
-	toggleSort int
+	toggleSort bool
 	expect     []int
+	keymap     map[int]actionType
 	pressed    int
 	printQuery bool
 	count      int
@@ -80,6 +81,87 @@ const (
 	reqQuit
 )
 
+type actionType int
+
+const (
+	actIgnore actionType = iota
+	actInvalid
+	actRune
+	actMouse
+	actBeginningOfLine
+	actAbort
+	actAccept
+	actBackwardChar
+	actBackwardDeleteChar
+	actBackwardWord
+	actClearScreen
+	actDeleteChar
+	actEndOfLine
+	actForwardChar
+	actForwardWord
+	actKillLine
+	actKillWord
+	actUnixLineDiscard
+	actUnixWordRubout
+	actYank
+	actBackwardKillWord
+	actToggleDown
+	actToggleUp
+	actDown
+	actUp
+	actPageUp
+	actPageDown
+	actToggleSort
+)
+
+func defaultKeymap() map[int]actionType {
+	keymap := make(map[int]actionType)
+	keymap[C.Invalid] = actInvalid
+	keymap[C.CtrlA] = actBeginningOfLine
+	keymap[C.CtrlB] = actBackwardChar
+	keymap[C.CtrlC] = actAbort
+	keymap[C.CtrlG] = actAbort
+	keymap[C.CtrlQ] = actAbort
+	keymap[C.ESC] = actAbort
+	keymap[C.CtrlD] = actDeleteChar
+	keymap[C.CtrlE] = actEndOfLine
+	keymap[C.CtrlF] = actForwardChar
+	keymap[C.CtrlH] = actBackwardDeleteChar
+	keymap[C.Tab] = actToggleDown
+	keymap[C.BTab] = actToggleUp
+	keymap[C.CtrlJ] = actDown
+	keymap[C.CtrlK] = actUp
+	keymap[C.CtrlL] = actClearScreen
+	keymap[C.CtrlM] = actAccept
+	keymap[C.CtrlN] = actDown
+	keymap[C.CtrlP] = actUp
+	keymap[C.CtrlU] = actUnixLineDiscard
+	keymap[C.CtrlW] = actUnixWordRubout
+	keymap[C.CtrlY] = actYank
+
+	keymap[C.AltB] = actBackwardWord
+	keymap[C.SLeft] = actBackwardWord
+	keymap[C.AltF] = actForwardWord
+	keymap[C.SRight] = actForwardWord
+	keymap[C.AltD] = actKillWord
+	keymap[C.AltBS] = actBackwardKillWord
+
+	keymap[C.Up] = actUp
+	keymap[C.Down] = actDown
+	keymap[C.Left] = actBackwardChar
+	keymap[C.Right] = actForwardChar
+
+	keymap[C.Home] = actBeginningOfLine
+	keymap[C.End] = actEndOfLine
+	keymap[C.Del] = actDeleteChar // FIXME Del vs. CTRL-D
+	keymap[C.PgUp] = actPageUp
+	keymap[C.PgDn] = actPageDown
+
+	keymap[C.Rune] = actRune
+	keymap[C.Mouse] = actMouse
+	return keymap
+}
+
 // NewTerminal returns new Terminal object
 func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 	input := []rune(opts.Query)
@@ -97,6 +179,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 		sort:       opts.Sort > 0,
 		toggleSort: opts.ToggleSort,
 		expect:     opts.Expect,
+		keymap:     opts.Keymap,
 		pressed:    0,
 		printQuery: opts.PrintQuery,
 		merger:     EmptyMerger,
@@ -249,7 +332,7 @@ func (t *Terminal) printInfo() {
 	}
 
 	output := fmt.Sprintf("%d/%d", t.merger.Length(), t.count)
-	if t.toggleSort > 0 {
+	if t.toggleSort {
 		if t.sort {
 			output += "/S"
 		} else {
@@ -601,105 +684,113 @@ func (t *Terminal) Loop() {
 				break
 			}
 		}
-		if t.toggleSort > 0 {
-			if keyMatch(t.toggleSort, event) {
-				t.sort = !t.sort
-				t.eventBox.Set(EvtSearchNew, t.sort)
-				t.mutex.Unlock()
-				continue
+
+		action := t.keymap[event.Type]
+		if event.Type == C.Rune {
+			code := int(event.Char) + int(C.AltZ)
+			if act, prs := t.keymap[code]; prs {
+				action = act
 			}
 		}
-		switch event.Type {
-		case C.Invalid:
+		switch action {
+		case actInvalid:
+			t.mutex.Unlock()
+			continue
+		case actToggleSort:
+			t.sort = !t.sort
+			t.eventBox.Set(EvtSearchNew, t.sort)
 			t.mutex.Unlock()
 			continue
-		case C.CtrlA:
+		case actBeginningOfLine:
 			t.cx = 0
-		case C.CtrlB:
+		case actBackwardChar:
 			if t.cx > 0 {
 				t.cx--
 			}
-		case C.CtrlC, C.CtrlG, C.CtrlQ, C.ESC:
+		case actAbort:
 			req(reqQuit)
-		case C.CtrlD:
+		case actDeleteChar:
 			if !t.delChar() && t.cx == 0 {
 				req(reqQuit)
 			}
-		case C.CtrlE:
+		case actEndOfLine:
 			t.cx = len(t.input)
-		case C.CtrlF:
+		case actForwardChar:
 			if t.cx < len(t.input) {
 				t.cx++
 			}
-		case C.CtrlH:
+		case actBackwardDeleteChar:
 			if t.cx > 0 {
 				t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
 				t.cx--
 			}
-		case C.Tab:
+		case actToggleDown:
 			if t.multi && t.merger.Length() > 0 {
 				toggle()
 				t.vmove(-1)
 				req(reqList)
 			}
-		case C.BTab:
+		case actToggleUp:
 			if t.multi && t.merger.Length() > 0 {
 				toggle()
 				t.vmove(1)
 				req(reqList)
 			}
-		case C.CtrlJ, C.CtrlN:
+		case actDown:
 			t.vmove(-1)
 			req(reqList)
-		case C.CtrlK, C.CtrlP:
+		case actUp:
 			t.vmove(1)
 			req(reqList)
-		case C.CtrlM:
+		case actAccept:
 			req(reqClose)
-		case C.CtrlL:
+		case actClearScreen:
 			req(reqRedraw)
-		case C.CtrlU:
+		case actUnixLineDiscard:
 			if t.cx > 0 {
 				t.yanked = copySlice(t.input[:t.cx])
 				t.input = t.input[t.cx:]
 				t.cx = 0
 			}
-		case C.CtrlW:
+		case actUnixWordRubout:
 			if t.cx > 0 {
 				t.rubout("\\s\\S")
 			}
-		case C.AltBS:
+		case actBackwardKillWord:
 			if t.cx > 0 {
 				t.rubout("[^[:alnum:]][[:alnum:]]")
 			}
-		case C.CtrlY:
+		case actYank:
 			suffix := copySlice(t.input[t.cx:])
 			t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
 			t.cx += len(t.yanked)
-		case C.Del:
-			t.delChar()
-		case C.PgUp:
+		case actPageUp:
 			t.vmove(t.maxItems() - 1)
 			req(reqList)
-		case C.PgDn:
+		case actPageDown:
 			t.vmove(-(t.maxItems() - 1))
 			req(reqList)
-		case C.AltB:
+		case actBackwardWord:
 			t.cx = findLastMatch("[^[:alnum:]][[:alnum:]]", string(t.input[:t.cx])) + 1
-		case C.AltF:
+		case actForwardWord:
 			t.cx += findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
-		case C.AltD:
+		case actKillWord:
 			ncx := t.cx +
 				findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
 			if ncx > t.cx {
 				t.yanked = copySlice(t.input[t.cx:ncx])
 				t.input = append(t.input[:t.cx], t.input[ncx:]...)
 			}
-		case C.Rune:
+		case actKillLine:
+			if t.cx < len(t.input) {
+				t.yanked = copySlice(t.input[t.cx:])
+				t.input = t.input[:t.cx]
+			}
+		case actRune:
 			prefix := copySlice(t.input[:t.cx])
 			t.input = append(append(prefix, event.Char), t.input[t.cx:]...)
 			t.cx++
-		case C.Mouse:
+		case actMouse:
 			me := event.MouseEvent
 			mx, my := util.Constrain(me.X-len(t.prompt), 0, len(t.input)), me.Y
 			if !t.reverse {
diff --git a/test/test_go.rb b/test/test_go.rb
index 36ef6854522715625690cf5e153a6f047cf57f56..53e2bc4e4363f9b745ac18d2ba2967ac5a1a13c0 100644
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -516,6 +516,12 @@ class TestGoFZF < TestBase
     assert_equal 1, `echo Foo bar | #{FZF} -x -f "foo Fbar" | wc -l`.to_i
   end
 
+  def test_bind
+    tmux.send_keys "seq 1 1000 | #{fzf '-m --bind=ctrl-j:accept,z:up,x:toggle-up'}", :Enter
+    tmux.until { |lines| lines[-2].end_with? '/1000' }
+    tmux.send_keys 'zzz', 'xx', 'C-j'
+    assert_equal %w[4 5], readonce.split($/)
+  end
 private
   def writelines path, lines
     File.unlink path while File.exists? path