Almost a year has passed since the question was asked. Well, the answer is not about mpld3 , but I do not stick to this particular technology. User @Drew suggested using bqplot , so I post a link to a connected laptop
https://github.com/bloomberg/bqplot/blob/master/examples/Interactions/Interaction%20Layer.ipynb
from bloomberg . If you open this, I recommend finding a link in the upper right corner that redirects you to an external nbviewer with images. Almost everything is contained there, I'm just trying to reproduce a minimalistic working example.
Please note that to run jupyter notebook with the bqplot extension, as well as for some ipywidgets you may need to do something like “magic” to make it work. You should be familiar with some bash commands like jupyter install nbextension and jupyter nbextension enable . I personally had to fight bqplot for several hours to get it working. But this is certainly a separate issue.
Make a trial attempt to run the observe function. The test function my_callback(...) simply prints events.
%matplotlib inline from bqplot import pyplot as plt def my_callback(change): print change scatt = plt.scatter([1,2,3],[4,5,6],enable_move=True) scatt.observe(my_callback) plt.show()
you will get a good plot like this: 
with the additional ability to drag points. After you drag the point, you will see a printed change list, which is a python structure, each event on a separate line.

{'owner' :, 'new': {u'hovered_point ': 1},' old ': traitlets.Undefined,' name ':' _property_lock ',' type ':' change '}
{'owner' :, 'new': 1, 'old': None, 'name': 'hovered_point', 'type': 'change'}
{'owner' :, 'new': {}, 'old': {u'hovered_point ': 1},' name ':' _property_lock ',' type ':' change '}
{'owner' :, 'new': {u'y ': {u'type': u'float ', u'values': [4, 4.863453784620906, 6]}, u'x': {u'type ': u'float', u'values': [1, 2.016078455307904, 3]}}, 'old': {}, 'name': '_property_lock', 'type': 'change'}
{'owner' :, 'new': array ([4., 4.86345378, 6.]), 'old': array ([4, 5, 6]), 'name': 'y', 'type': 'change'}
{'owner' :, 'new': array ([1., 2.01607846, 3.]), 'old': array ([1, 2, 3]), 'name': 'x', 'type': 'change'}
{'owner' :, 'new': {}, 'old': {u'y ': {u'type': u'float ', u'values': [4, 4.863453784620906, 6]}, u' x ': {u'type': u'float ', u'values': [1, 2.016078455307904, 3]}}, 'name': '_property_lock', 'type': 'change'}
{'owner' :, 'new': {u'hovered_point ': None},' old ': {},' name ':' _property_lock ',' type ':' change '}
{'owner' :, 'new': None, 'old': 1, 'name': 'hovered_point', 'type': 'change'}
{'owner' :, 'new': {}, 'old': {u'hovered_point ': None},' name ':' _property_lock ',' type ':' change '}
I admit that the structure is a little complicated to decompose, but after some careful searching, note that the bold line has a 'name' equal to '_property_lock' , then the substructure 'new' contains the fields u'x' and u'y' , which is Unicode for "x" and "y".
Then you can track these changes and, accordingly, run some python code inside the my_callback(...) function, you can even draw something inside this plot or create a new one, etc. Surprisingly, this somehow works, and with the new jupyter you can even save your laptop with widgets that are completely blurry.