diff --git a/plugins/jspsych-button-response.js b/plugins/jspsych-button-response.js
new file mode 100644
index 00000000..e33ba005
--- /dev/null
+++ b/plugins/jspsych-button-response.js
@@ -0,0 +1,158 @@
+/**
+ * jspsych-button-response
+ * Josh de Leeuw
+ *
+ * plugin for displaying a stimulus and getting a keyboard response
+ *
+ * documentation: docs.jspsych.org
+ *
+ **/
+
+(function($) {
+ jsPsych["button-response"] = (function() {
+
+ var plugin = {};
+
+ plugin.create = function(params) {
+
+ params = jsPsych.pluginAPI.enforceArray(params, ['stimuli', 'choices']);
+
+ var trials = new Array(params.stimuli.length);
+ for (var i = 0; i < trials.length; i++) {
+ trials[i] = {};
+ trials[i].a_path = params.stimuli[i];
+ trials[i].choices = params.choices;
+ trials[i].button_html = params.button_html || '';
+ trials[i].response_ends_trial = (typeof params.response_ends_trial === 'undefined') ? true : params.response_ends_trial;
+ // timing parameters
+ trials[i].timing_stim = params.timing_stim || -1; // if -1, then show indefinitely
+ trials[i].timing_response = params.timing_response || -1; // if -1, then wait for response forever
+ // optional parameters
+ trials[i].is_html = (typeof params.is_html === 'undefined') ? false : params.is_html;
+ trials[i].prompt = (typeof params.prompt === 'undefined') ? "" : params.prompt;
+ }
+ return trials;
+ };
+
+
+
+ plugin.trial = function(display_element, trial) {
+
+ // if any trial variables are functions
+ // this evaluates the function and replaces
+ // it with the output of the function
+ trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
+
+ // this array holds handlers from setTimeout calls
+ // that need to be cleared if the trial ends early
+ var setTimeoutHandlers = [];
+
+ // display stimulus
+ if (!trial.is_html) {
+ display_element.append($('', {
+ src: trial.a_path,
+ id: 'jspsych-button-response-stimulus'
+ }));
+ } else {
+ display_element.append($('