diff --git a/toonz/sources/tnztools/modifiers/modifiertest.cpp b/toonz/sources/tnztools/modifiers/modifiertest.cpp
index 72f4774..ba33792 100644
--- a/toonz/sources/tnztools/modifiers/modifiertest.cpp
+++ b/toonz/sources/tnztools/modifiers/modifiertest.cpp
@@ -26,18 +26,19 @@ TTrackPoint TModifierTest::Interpolator::interpolateFromOriginal(double original
 
   double angle = TTrack::interpolationLinear(
     handler->angles[i0], handler->angles[i1], frac );
-  angle = angle*speed + this->angle;
-  
-  double r = radius*p.pressure;
-  double s = sin(angle);
+  double a = angle*speed + this->angle;
+
+  double kr = 1 - 1/(angle + 1);
+  double s = sin(a);
+  double r = radius*p.pressure*kr*s;
   
   double d = fabs(2.0*radius);
   if (fabs(speed) > TConsts::epsilon)
     d /= fabs(speed);
 
   TPointD tangent = track.original->calcTangent(originalIndex, d);
-  p.position.x -= tangent.y * s * r;
-  p.position.y += tangent.x * s * r;
+  p.position.x -= tangent.y * r;
+  p.position.y += tangent.x * r;
   p.pressure *= fabs(s);
 
   return p;
diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.cpp b/toonz/sources/tnztools/toonzvectorbrushtool.cpp
index b7eb5d6..1e01069 100644
--- a/toonz/sources/tnztools/toonzvectorbrushtool.cpp
+++ b/toonz/sources/tnztools/toonzvectorbrushtool.cpp
@@ -508,7 +508,6 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType)
     , m_joinStyle("Join")
     , m_miterJoinLimit("Miter:", 0, 100, 4)
     , m_assistants("Assistants", true)
-    , m_firstStroke()
     , m_styleId()
     , m_minThick()
     , m_maxThick()
@@ -522,7 +521,6 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType)
     , m_snapped()
     , m_snappedSelf()
     , m_active()
-    , m_isPrompting()
     , m_firstTime(true)
     , m_isPath()
     , m_presetsLoaded()
@@ -741,6 +739,23 @@ void ToonzVectorBrushTool::inputMouseMove(
 
 //--------------------------------------------------------------------------------------------------
 
+void ToonzVectorBrushTool::deleteStrokes(StrokeList &strokes) {
+  for(StrokeList::iterator i = strokes.begin(); i != strokes.end(); ++i)
+    delete *i;
+  strokes.clear();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ToonzVectorBrushTool::copyStrokes(StrokeList &dst, const StrokeList &src) {
+  deleteStrokes(dst);
+  dst.reserve(src.size());
+  for(StrokeList::const_iterator i = src.begin(); i != src.end(); ++i)
+    dst.push_back(new TStroke(**i));
+}
+
+//--------------------------------------------------------------------------------------------------
+
 void ToonzVectorBrushTool::inputSetBusy(bool busy) {
   if (m_active == busy) return;
   
@@ -748,6 +763,9 @@ void ToonzVectorBrushTool::inputSetBusy(bool busy) {
     
     // begin painting //////////////////////////
     
+    m_styleId = 0;
+    m_tracks.clear();
+    
     TTool::Application *app = TTool::getApplication();
     if (!app)
       return;
@@ -795,26 +813,32 @@ void ToonzVectorBrushTool::inputSetBusy(bool busy) {
   // clear tracks automatically when return from this function
   struct Cleanup {
     ToonzVectorBrushTool &owner;
-    inline ~Cleanup() { owner.m_track.clear(); owner.invalidate(); }
+    inline ~Cleanup() { owner.m_tracks.clear(); owner.invalidate(); }
   } cleanup = {*this};
- 
+
+  // remove empty tracks
+  for(TrackList::iterator i = m_tracks.begin(); i != m_tracks.end(); )
+    if (i->isEmpty()) i = m_tracks.erase(i); else ++i;
+  
+  if (m_tracks.empty())
+    return;
   
   // make motion path (if need)
     
   if (m_isPath) {
     double error = 20.0 * m_pixelSize;
 
-    TStroke *stroke = m_track.makeStroke(error);
+    m_tracks.resize(1);
+    TStroke *stroke = m_tracks.front().makeStroke(error);
 
     TVectorImageP vi = getImage(true);
 
     if (!isJustCreatedSpline(vi.getPointer())) {
-      m_isPrompting = true;
+      m_currentColor = TPixel32::Green;
       invalidate();
       int ret = DVGui::MsgBox(
         QString("Are you sure you want to replace the motion path?"),
         QObject::tr("Yes"), QObject::tr("No"), 0 );
-      m_isPrompting = false;
       if (ret != 1)
         return; // 1 here means "Yes" button
     }
@@ -833,51 +857,54 @@ void ToonzVectorBrushTool::inputSetBusy(bool busy) {
     return; // done with motion path
   }
   
-  
   // paint regular strokes
-
-  if (m_track.isEmpty())
-    return;
-
-  
-  // prepare stroke
-  
-  m_track.filterPoints();
-  double error = 30.0/(1 + 0.5 * m_accuracy.getValue())*m_pixelSize;
-  TStroke *stroke = m_track.makeStroke(error);
-  
-  stroke->setStyle(m_styleId);
-  m_styleId = 0;
   
-  TStroke::OutlineOptions &options = stroke->outlineOptions();
-  options.m_capStyle   = m_capStyle.getIndex();
-  options.m_joinStyle  = m_joinStyle.getIndex();
-  options.m_miterUpper = m_miterJoinLimit.getValue();
-
-  if ( stroke->getControlPointCount() == 3
-    && stroke->getControlPoint(0) != stroke->getControlPoint(2) )
-                                       // gli stroke con solo 1 chunk vengono
-                                       // fatti dal tape tool...e devono venir
-                                       // riconosciuti come speciali di
-                                       // autoclose proprio dal fatto che
-                                       // hanno 1 solo chunk.
-    stroke->insertControlPoints(0.5);
-
-  
-  // lock image
-
   TVectorImageP vi = getImage(true);
   QMutexLocker lock(vi->getMutex());
   TTool::Application *app = TTool::getApplication();
   
-  
+  // prepare strokes
+    
+  StrokeList strokes;
+  strokes.reserve(m_tracks.size());
+  for(TrackList::iterator i = m_tracks.begin(); i != m_tracks.end(); ++i) {
+    StrokeGenerator &track = *i;
+    
+    track.filterPoints();
+    double error = 30.0/(1 + 0.5 * m_accuracy.getValue())*m_pixelSize;
+    TStroke *stroke = track.makeStroke(error);
+    
+    stroke->setStyle(m_styleId);
+    
+    TStroke::OutlineOptions &options = stroke->outlineOptions();
+    options.m_capStyle   = m_capStyle.getIndex();
+    options.m_joinStyle  = m_joinStyle.getIndex();
+    options.m_miterUpper = m_miterJoinLimit.getValue();
+
+    if ( stroke->getControlPointCount() == 3
+      && stroke->getControlPoint(0) != stroke->getControlPoint(2) )
+                                        // gli stroke con solo 1 chunk vengono
+                                        // fatti dal tape tool...e devono venir
+                                        // riconosciuti come speciali di
+                                        // autoclose proprio dal fatto che
+                                        // hanno 1 solo chunk.
+      stroke->insertControlPoints(0.5);
+    
+    if (!m_frameRange.getIndex() && track.getLoop())
+      stroke->setSelfLoop(true);
+    
+    strokes.push_back(stroke);
+  }
+
   // add stroke to image
   
   if (m_frameRange.getIndex()) {
+    // frame range stroke
     if (m_firstFrameId == -1) {
-      m_firstStroke  = new TStroke(*stroke);
+      // remember strokes for first srame
+      copyStrokes(m_firstStrokes, strokes);
       m_firstFrameId = getFrameId();
-      m_rangeTrack = m_track;
+      m_rangeTracks  = m_tracks;
       
       if (app) {
         m_col        = app->getCurrentColumn()->getColumnIndex();
@@ -891,18 +918,25 @@ void ToonzVectorBrushTool::inputSetBusy(bool busy) {
       }
     } else
     if (m_firstFrameId == getFrameId()) {
-      if (m_firstStroke) delete m_firstStroke;
-      m_firstStroke = new TStroke(*stroke);
-      m_rangeTrack  = m_track;
+      // painted of first frame agein, so
+      // just replace the remembered strokes for first frame
+      copyStrokes(m_firstStrokes, strokes);
+      m_rangeTracks = m_tracks;
     } else {
+      // paint frame range strokes
       TFrameId currentId = getFrameId();
       int curCol   = app ? app->getCurrentColumn()->getColumnIndex() : 0;
       int curFrame = app ? app->getCurrentFrame()->getFrame()        : 0;
       
-      doFrameRangeStrokes(
-          m_firstFrameId, m_firstStroke, getFrameId(), stroke,
-          m_frameRange.getIndex(), m_breakAngles.getValue(), false, false,
-          m_firstFrameRange );
+      if (size_t count = std::min(m_firstStrokes.size(), strokes.size())) {
+        TUndoManager::manager()->beginBlock();
+        for(size_t i = 0; i < count; ++i)
+          doFrameRangeStrokes(
+              m_firstFrameId, m_firstStrokes[i], getFrameId(), strokes[i],
+              m_frameRange.getIndex(), m_breakAngles.getValue(), false, false,
+              m_firstFrameRange );
+        TUndoManager::manager()->endBlock();
+      }
       
       if (m_inputmanager.state.isKeyPressed(TInputState::Key::control)) {
         if (app && m_firstFrameId > currentId) {
@@ -915,8 +949,8 @@ void ToonzVectorBrushTool::inputSetBusy(bool busy) {
         }
         
         resetFrameRange();
-        m_firstStroke     = new TStroke(*stroke);
-        m_rangeTrack      = m_track;
+        copyStrokes(m_firstStrokes, strokes);
+        m_rangeTracks     = m_tracks;
         m_firstFrameId    = currentId;
         m_firstFrameRange = false;
       } else {
@@ -932,27 +966,30 @@ void ToonzVectorBrushTool::inputSetBusy(bool busy) {
       }
     }
   } else {
-    if (m_track.getLoop())
-      stroke->setSelfLoop(true);
-
-    addStrokeToImage(app, vi, stroke, m_breakAngles.getValue(),
-                     false, false, m_isFrameCreated, m_isLevelCreated);
-
-    if ((Preferences::instance()->getGuidedDrawingType() == 1 ||
-         Preferences::instance()->getGuidedDrawingType() == 2) &&
-        Preferences::instance()->getGuidedAutoInbetween())
-    {
-      TFrameId fId = getFrameId();
-      doGuidedAutoInbetween(fId, vi, stroke, m_breakAngles.getValue(), false,
-                            false, false);
-      if (app->getCurrentFrame()->isEditingScene())
-        app->getCurrentFrame()->setFrame( app->getCurrentFrame()->getFrameIndex() );
-      else
-        app->getCurrentFrame()->setFid(fId);
+    // regular paint strokes
+    TUndoManager::manager()->beginBlock();
+    for(StrokeList::iterator i = strokes.begin(); i != strokes.end(); ++i) {
+      TStroke *stroke = *i;
+      addStrokeToImage(app, vi, stroke, m_breakAngles.getValue(),
+                      false, false, m_isFrameCreated, m_isLevelCreated);
+
+      if ((Preferences::instance()->getGuidedDrawingType() == 1 ||
+          Preferences::instance()->getGuidedDrawingType() == 2) &&
+          Preferences::instance()->getGuidedAutoInbetween())
+      {
+        TFrameId fId = getFrameId();
+        doGuidedAutoInbetween(fId, vi, stroke, m_breakAngles.getValue(), false,
+                              false, false);
+        if (app->getCurrentFrame()->isEditingScene())
+          app->getCurrentFrame()->setFrame( app->getCurrentFrame()->getFrameIndex() );
+        else
+          app->getCurrentFrame()->setFid(fId);
+      }
     }
+    TUndoManager::manager()->endBlock();
   }
   
-  delete stroke;
+  deleteStrokes(strokes);
 }
 
 //--------------------------------------------------------------------------------------------------
@@ -961,32 +998,39 @@ void ToonzVectorBrushTool::inputPaintTracks(const TTrackList &tracks) {
   if (tracks.empty()) return;
 
   TRectD invalidateRect;
-  
-  const TTrack &track = *tracks.front();
-  while(track.pointsRemoved) {
-    m_track.pop();
-    --track.pointsRemoved;
-  }
-  
-  while(track.pointsAdded) {
-    const TTrackPoint &p = track.current();
-    double t = computeThickness(p.pressure, m_thickness, m_pressure.getValue(), m_isPath);
-    m_track.add(TThickPoint(p.position, t), 0);
-    --track.pointsAdded;
-  }
-  
-  bool loop = m_snappedSelf
-           && track.fixedFinished()
-           && !track.empty()
-           && areAlmostEqual(track.front().position, track.back().position);
-  m_track.setLoop(loop);
-  
-  invalidateRect += m_track.getLastModifiedRegion();
 
-  TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5);
-  invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick);
-  m_brushPos = m_mousePos = track.current().position;
-  invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick);
+  size_t count = m_isPath ? 1 : tracks.size();
+  m_tracks.resize(count);
+  for(size_t i = 0; i < count; ++i) {
+    const TTrack &track = *tracks[i];
+    StrokeGenerator &gen = m_tracks[i];
+    
+    while(track.pointsRemoved) {
+      gen.pop();
+      --track.pointsRemoved;
+    }
+    
+    while(track.pointsAdded) {
+      const TTrackPoint &p = track.current();
+      double t = computeThickness(p.pressure, m_thickness, m_pressure.getValue(), m_isPath);
+      gen.add(TThickPoint(p.position, t), 0);
+      --track.pointsAdded;
+    }
+    
+    bool loop = m_snappedSelf
+            && track.fixedFinished()
+            && !track.empty()
+            && areAlmostEqual(track.front().position, track.back().position);
+    gen.setLoop(loop);
+    
+    invalidateRect += gen.getLastModifiedRegion();
+    if (!i) {
+      TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5);
+      invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick);
+      m_brushPos = m_mousePos = track.current().position;
+      invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick);
+    }
+  }
   
   if (!invalidateRect.isEmpty()) {
     if (m_isPath) {
@@ -1080,7 +1124,7 @@ void ToonzVectorBrushTool::handleMouseEvent(MouseEventType type,
     m_inputmanager.hoverEvent(hovers);
   } else
   if (pickerMode) {
-    if (type == ME_DOWN) getViewer()->doPickGuideStroke(snappedPos);
+    if (type == ME_DOWN) getViewer()->doPickGuideStroke(pos);
   } else {
     int    deviceId    = e.isTablet() ? 1 : 0;
     bool   hasPressure = e.isTablet();
@@ -1382,8 +1426,8 @@ void ToonzVectorBrushTool::snap(const TPointD &pos, bool snapEnabled, bool withS
       }
     
       // finally snap to first point of track (self snap)
-      if (withSelfSnap && !m_track.isEmpty()) {
-        TPointD p = m_track.getFirstPoint();
+      if (withSelfSnap && !m_tracks.empty() && !m_tracks.front().isEmpty()) {
+        TPointD p = m_tracks.front().getFirstPoint();
         double d2 = tdistance2(pos, p);
         if (d2 < minDistance2) {
           m_snappedSelf = true;
@@ -1428,8 +1472,9 @@ void ToonzVectorBrushTool::draw() {
     return;
 
   // draw track
-  tglColor(m_isPrompting ? TPixel32::Green : m_currentColor);
-  m_track.drawAllFragments();
+  tglColor(m_currentColor);
+  for(TrackList::iterator i = m_tracks.begin(); i != m_tracks.end(); ++i)
+    i->drawAllFragments();
   
   // draw snapping
   double snapMarkRadius = 6.0 * m_pixelSize;
@@ -1443,13 +1488,14 @@ void ToonzVectorBrushTool::draw() {
   }
 
   // frame range
-  if (m_firstStroke) {
-    glColor3d(1.0, 0.0, 0.0);
-    m_rangeTrack.drawAllFragments();
-    glColor3d(0.0, 0.6, 0.0);
+  for(TrackList::iterator i = m_rangeTracks.begin(); i != m_rangeTracks.end(); ++i) {
+    if (i->isEmpty()) continue;
     TPointD offset1 = TPointD(5, 5);
     TPointD offset2 = TPointD(-offset1.x, offset1.y);
-    TPointD point = m_rangeTrack.getFirstPoint();
+    TPointD point = i->getFirstPoint();
+    glColor3d(1.0, 0.0, 0.0);
+    i->drawAllFragments();
+    glColor3d(0.0, 0.6, 0.0);
     tglDrawSegment(point - offset1, point + offset1);
     tglDrawSegment(point - offset2, point + offset2);
   }
@@ -1500,12 +1546,9 @@ TPropertyGroup *ToonzVectorBrushTool::getProperties(int idx) {
 //------------------------------------------------------------------
 
 void ToonzVectorBrushTool::resetFrameRange() {
-  m_rangeTrack.clear();
+  m_rangeTracks.clear();
   m_firstFrameId = -1;
-  if (m_firstStroke) {
-    delete m_firstStroke;
-    m_firstStroke = nullptr;
-  }
+  deleteStrokes(m_firstStrokes);
   m_firstFrameRange = true;
 }
 
diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.h b/toonz/sources/tnztools/toonzvectorbrushtool.h
index 1ae9926..579894a 100644
--- a/toonz/sources/tnztools/toonzvectorbrushtool.h
+++ b/toonz/sources/tnztools/toonzvectorbrushtool.h
@@ -158,6 +158,11 @@ public:
                              bool drawStroke = true);
 
 protected:
+  typedef std::vector<StrokeGenerator> TrackList;
+  typedef std::vector<TStroke*> StrokeList;
+  void deleteStrokes(StrokeList &strokes);
+  void copyStrokes(StrokeList &dst, const StrokeList &src);
+
   void snap(const TPointD &pos, bool snapEnabled, bool withSelfSnap = false);
 
   enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE };
@@ -192,10 +197,10 @@ protected:
 #ifndef NDEBUG
   TSmartPointerT<TModifierTest> m_modifierTest;
 #endif
-
-  StrokeGenerator m_track;
-  StrokeGenerator m_rangeTrack;
-  TStroke *m_firstStroke;
+  
+  TrackList m_tracks;
+  TrackList m_rangeTracks;
+  StrokeList m_firstStrokes;
   TFrameId m_firstFrameId, m_veryFirstFrameId;
   TPixel32 m_currentColor;
   int m_styleId; // bwtodo: remove
@@ -217,10 +222,8 @@ protected:
   VectorBrushPresetManager
       m_presetsManager;  //!< Manager for presets of this tool instance
 
-  bool m_active,
-      m_isPrompting,  //!< Whether the tool is prompting for spline
-                      //! substitution.
-      m_firstTime, m_isPath, m_presetsLoaded, m_firstFrameRange;
+  bool m_active, m_firstTime, m_isPath,
+       m_presetsLoaded, m_firstFrameRange;
 
   bool m_propertyUpdating;
 };