diff --git a/flatland/utils/editor.py b/flatland/utils/editor.py
index a09b131eb6e72ac3a4445b65da4d3cbca4b4d6c1..f44deda7dc0d63512ee9926825a987b76409f2b5 100644
--- a/flatland/utils/editor.py
+++ b/flatland/utils/editor.py
@@ -46,7 +46,7 @@ class JupEditor(object):
         self.yxBase = array([6, 21])  # pixel offset
         self.nPixCell = 700 / self.env.rail.width  # 35
 
-        self.rcHistory = []
+        self.lrcStroke = []
         self.iTransLast = -1
         self.gRCTrans = array([[-1, 0], [0, 1], [1, 0], [0, -1]])  # NESW in RC
 
@@ -112,19 +112,25 @@ class JupEditor(object):
         y = event['canvasY']
         env = self.env
         qEvents = self.qEvents
-        rcHistory = self.rcHistory
+        lrcStroke = self.lrcStroke
         bRedrawn = False
         writableData = None
 
         if self.debug and (event["buttons"] > 0 or self.debug_move):
-            self.log("debug:", len(qEvents), len(rcHistory), event)
+            self.log("debug:", len(qEvents), len(lrcStroke), event)
 
         assert wid == self.wid_img, "wid not same as wid_img"
 
         # If the mouse is held down, enqueue an event in our own queue
+        # The intention was to avoid too many redraws.
         if event["buttons"] > 0:
             qEvents.append((time.time(), x, y))
         
+        # Process the events in our queue:
+        # Draw a black square to indicate a trail
+        # TODO: infer a vector of moves between these squares to avoid gaps
+        # Convert the xy position to a cell rc
+        # Enqueue transitions across cells in another queue
         if len(qEvents) > 0:
             tNow = time.time()
             if tNow - qEvents[0][0] > 0.1:   # wait before trying to draw
@@ -142,93 +148,39 @@ class JupEditor(object):
                         # Translate and scale from x,y to integer row,col (note order change)
                         rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int)
 
-                        if len(rcHistory) > 1:
-                            rcLast = rcHistory[-1]
+                        # Store the row,col location of the click, if we have entered a new cell
+                        if len(lrcStroke) > 0:
+                            rcLast = lrcStroke[-1]
                             if not np.array_equal(rcLast, rcCell):  # only save at transition
                                 # print(y, x, rcCell)
-                                rcHistory.append(rcCell)
+                                lrcStroke.append(rcCell)
                         else:
-                            rcHistory.append(rcCell)
-
-        elif len(rcHistory) >= 3:
+                            # This is the first cell in a mouse stroke
+                            lrcStroke.append(rcCell)
+
+        # This elif means we wait until all the mouse events have been processed (black square drawn)
+        # before trying to draw rails.  (We could change this behaviour)
+        # Equivalent to waiting for mouse button to be lifted (and a mouse event is necessary:
+        # the mouse may need to be moved)
+        elif len(lrcStroke) >= 3:
             # If we have already touched 3 cells
             # We have a transition into a cell, and out of it.
             
             if self.drawMode == "Draw":
-                bTransition = True
+                bAddRemove = True
             elif self.drawMode == "Erase":
-                bTransition = False
-
-            while len(rcHistory) >= 3:
-                rc3Cells = array(rcHistory[:3])  # the 3 cells
-                rcMiddle = rc3Cells[1]  # the middle cell which we will update
+                bAddRemove = False
 
-                # Save the original state of the cell
-                oTransrcMiddle = self.env.rail.get_transitions(rcMiddle)
-                sTransrcMiddle = self.env.rail.cell_repr(rcMiddle)
+            # If the first cell in a stroke is empty, add a deadend to cell 0
+            if self.env.rail.get_transitions(lrcStroke[0]) == 0:
+                self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=0)
 
-                # get the 2 row, col deltas between the 3 cells, eg [-1,0] = North
-                rc2Trans = np.diff(rc3Cells, axis=0)
-                
-                # get the direction index for the 2 transitions
-                liTrans = []
-                for rcTrans in rc2Trans:
-                    # gRCTrans - rcTrans gives an array of vector differences between our rcTrans
-                    # and the 4 directions stored in gRCTrans.
-                    # Where the vector difference is zero, we have a match...
-                    # np.all detects where the whole row,col vector is zero.
-                    # argwhere gives the index of the zero vector, ie the direction index
-                    iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
-                    if len(iTrans) > 0:
-                        iTrans = iTrans[0][0]
-                        liTrans.append(iTrans)
-
-                # check that we have two transitions
-                if len(liTrans) == 2:
-                    # Set the transition
-                    env.rail.set_transition((*rcMiddle, liTrans[0]), liTrans[1], bTransition)
-
-                    # Also set the reverse transition
-                    # use the reversed outbound transition for inbound
-                    # and the reversed inbound transition for outbound
-                    env.rail.set_transition((*rcMiddle, mirror(liTrans[1])), mirror(liTrans[0]), bTransition)
-
-                    bValid = env.rail.is_cell_valid(rcMiddle)
-                    if not bValid:
-                        # Reset cell transition values
-                        env.rail.grid[tuple(rcMiddle)] = oTransrcMiddle
-
-                self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
-                rcHistory.pop(0)  # remove the last-but-one
+            while len(lrcStroke) >= 3:
+                self.add_rail_3cells(lrcStroke, env, bAddRemove)
 
             # If final cell empty, insert deadend:
-            if len(rcHistory) == 2 and (self.env.rail.get_transitions(rcHistory[1]) == 0):
-                rc2Cells = array(rcHistory[:2])  # the 2 cells
-                rcFinal = rc2Cells[1]  # the final cell which we will update
-
-                # get the row, col delta between the 2 cells, eg [-1,0] = North
-                rc2Trans = np.diff(rc2Cells, axis=0)
-                
-                # get the direction index for the 2 transitions
-                liTrans = []
-                for rcTrans in rc2Trans:
-                    iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
-                    if len(iTrans) > 0:
-                        iTrans = iTrans[0][0]
-                        liTrans.append(iTrans)
-
-                # check that we have one transition
-                if len(liTrans) == 1:
-                    # Set the transition as a deadend
-                    env.rail.set_transition((*rcFinal, liTrans[0]), mirror(liTrans[0]), bTransition)
-
-                    bValid = env.rail.is_cell_valid(rcMiddle)
-                    if not bValid:
-                        # Reset cell transition values
-                        env.rail.grid[tuple(rcMiddle)] = oTransrcMiddle
-
-                self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
-                rcHistory.pop(0)  # remove the last-but-one
+            if len(lrcStroke) == 2 and (self.env.rail.get_transitions(lrcStroke[1]) == 0):
+                self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=1)
 
             self.redraw()
             bRedrawn = True
@@ -237,7 +189,100 @@ class JupEditor(object):
         if not bRedrawn and writableData is not None:
             # This updates the image in the browser to be the new edited version
             self.wid_img.data = writableData
-    
+
+    def add_rail_3cells(self, rcCells, bAddRemove=True, bPop=True):
+        """
+        Add transitions for rail spanning three cells.
+        lrcCells -- list of 3 rc cells
+        bAddRemove -- whether to add (True) or remove (False) the transition
+        The transition is added to or removed from the 2nd cell, consistent with
+        entering from the 1st cell, and exiting into the 3rd.
+        Both the forward and backward transitions are added,
+        eg rcCells [(3,4), (2,4), (2,5)] would result in the transitions
+        N->E and W->S in cell (2,4).
+        """
+        rc3Cells = array(rcCells[:3])  # the 3 cells
+        rcMiddle = rc3Cells[1]  # the middle cell which we will update
+        bDeadend = np.all(rcCells[0] == rcCells[2])  # deadend means cell 0 == cell 2
+
+        # Save the original state of the cell
+        # oTransrcMiddle = self.env.rail.get_transitions(rcMiddle)
+        # sTransrcMiddle = self.env.rail.cell_repr(rcMiddle)
+
+        # get the 2 row, col deltas between the 3 cells, eg [-1,0] = North
+        rc2Trans = np.diff(rc3Cells, axis=0)
+
+        # get the direction index for the 2 transitions
+        liTrans = []
+        for rcTrans in rc2Trans:
+            # gRCTrans - rcTrans gives an array of vector differences between our rcTrans
+            # and the 4 directions stored in gRCTrans.
+            # Where the vector difference is zero, we have a match...
+            # np.all detects where the whole row,col vector is zero.
+            # argwhere gives the index of the zero vector, ie the direction index
+            iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
+            if len(iTrans) > 0:
+                iTrans = iTrans[0][0]
+                liTrans.append(iTrans)
+
+        # check that we have two transitions
+        if len(liTrans) == 2:
+            # Set the transition
+            # If this transition spans 3 cells, it is not a deadend, so remove any deadends.
+            # The user will need to resolve any conflicts.
+            self.env.rail.set_transition((*rcMiddle, liTrans[0]), liTrans[1], bAddRemove,
+                remove_deadends=not bDeadend)
+
+            # Also set the reverse transition
+            # use the reversed outbound transition for inbound
+            # and the reversed inbound transition for outbound
+            self.env.rail.set_transition((*rcMiddle, mirror(liTrans[1])),
+                mirror(liTrans[0]), bAddRemove, remove_deadends=not bDeadend)
+
+            # bValid = self.env.rail.is_cell_valid(rcMiddle)
+            # if not bValid:
+            #    # Reset cell transition values
+            #    self.env.rail.grid[tuple(rcMiddle)] = oTransrcMiddle
+
+        # self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
+        if bPop:
+            rcCells.pop(0)  # remove the first cell in the stroke
+
+    def add_rail_2cells(self, lrcCells, bAddRemove=True, iCellToMod=0, bPop=False):
+        """
+        Add transitions for rail between two cells
+        lrcCells -- list of two rc cells
+        bAddRemove -- whether to add (True) or remove (False) the transition
+        iCellToMod -- the index of the cell to modify: either 0 or 1
+        """
+        rc2Cells = array(lrcCells[:2])  # the 2 cells
+        rcMod = rc2Cells[iCellToMod]  # the cell which we will update
+
+        # get the row, col delta between the 2 cells, eg [-1,0] = North
+        rc1Trans = np.diff(rc2Cells, axis=0)
+        
+        # get the direction index for the transition
+        liTrans = []
+        for rcTrans in rc1Trans:
+            iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
+            if len(iTrans) > 0:
+                iTrans = iTrans[0][0]
+                liTrans.append(iTrans)
+
+        # check that we have one transition
+        if len(liTrans) == 1:
+            # Set the transition as a deadend
+            # The transition is going from cell 0 to cell 1.
+            if iCellToMod == 0:
+                # if 0, reverse the transition, we need to be entering cell 0
+                self.env.rail.set_transition((*rcMod, mirror(liTrans[0])), liTrans[0], bAddRemove)
+            else:
+                # if 1, the transition is entering cell 1
+                self.env.rail.set_transition((*rcMod, liTrans[0]), mirror(liTrans[0]), bAddRemove)
+
+        if bPop:
+            lrcCells.pop(0)
+
     def redraw(self, hide_stdout=True, update=True):
 
         # if hide_stdout: