diff --git a/mono/Assistance/Assistance.csproj b/mono/Assistance/Assistance.csproj
index e2127d1..f8b5b22 100644
--- a/mono/Assistance/Assistance.csproj
+++ b/mono/Assistance/Assistance.csproj
@@ -60,10 +60,11 @@
     </Compile>
     <Compile Include="Timer.cs" />
     <Compile Include="InputState.cs" />
-    <Compile Include="InputModifier.cs" />
     <Compile Include="InputModifierAssistants.cs" />
     <Compile Include="InputManager.cs" />
     <Compile Include="KeyHistory.cs" />
+    <Compile Include="InputModifierTangents.cs" />
+    <Compile Include="InputModifierInterpolation.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
diff --git a/mono/Assistance/InputManager.cs b/mono/Assistance/InputManager.cs
index 059f543..99b0acf 100644
--- a/mono/Assistance/InputManager.cs
+++ b/mono/Assistance/InputManager.cs
@@ -7,8 +7,9 @@ namespace Assistance {
 
 		public class TrackHandler: Track.Handler {
 			public readonly List<int> keys = new List<int>();
-			public TrackHandler(InputManager owner, Track original):
-				base(owner, track) { }
+			public TrackHandler(InputManager owner, Track original, int keysCount = 0):
+				base(owner, track)
+				{ for(int i = 0; i < keysCount; ++i) keys.Add(0); }
 		}
 		
 		public class KeyPoint {
@@ -31,19 +32,30 @@ namespace Assistance {
 				{ get { return refCount <= 0; } }
 		}
 		
+		public class Modifier: Track.Owner {
+			public virtual void activate() { }
+			public virtual void modify(Track tracks, KeyPoint keyPoint, List<Track> outTracks)
+				{ }
+			public virtual void modify(List<Track> tracks, KeyPoint keyPoint, List<Track> outTracks)
+				{ foreach(Track track in tracks) modify(track, keyPoint, outTracks); }
+			public virtual void deactivate() { }
+		}
+
 		
 		public readonly Workarea workarea;
 		
 		private readonly InputState state = new InputState();
 
 		private Tool tool;
-		private readonly List<InputModifier> modifiers = new List<InputModifier>();
+		private readonly List<Modifier> modifiers = new List<Modifier>();
 
 		private readonly List<Track> tracks = new List<Track>();
-		private readonly List<Track> subTracks = null;
 		private readonly List<KeyPoint> keyPoints = new List<KeyPoint>();
 		private int keyPointsSent;
 
+		private List<Track> subTracks = null;
+		private readonly List<Track>[] subTracksBuf = new List<Track>[] { new List<Track>(), new List<Track>() };
+
 
 		InputManager(Workarea workarea)
 			{ this.workarea = workarea; }
@@ -106,9 +118,19 @@ namespace Assistance {
 			while(true) {
 				// run modifiers
 				KeyPoint newKeyPoint = new KeyPoint();
-				List<Track> subTracks = tracks;
-				foreach(Modifier modifier in modifiers)
-					subTracks = modifier.modify(subTracks);
+				subTracks = tracks;
+				int i = 0;
+				foreach(Modifier modifier in modifiers) {
+					List<Track> outTracks = subTracksBuf[i];
+					modifier.modify(subTracks, keyEvent, outTracks);
+					subTracks = outTracks;
+					i = 1 - i;
+				}
+				
+				// create handlers	
+				foreach(Track track in subTracks)
+					if (track.handler == null)
+						track.handler = new TrackHandler(this, track, keyPoints.Count);
 	
 				if (keyPoints.Count > 0) {
 					// rollback
@@ -277,15 +299,15 @@ namespace Assistance {
 		
 		public int getModifiersCount()
 			{ return modifiers.Count; }
-		public InputModifier getModifier(int index)
+		public Modifier getModifier(int index)
 			{ return modifiers[index]; }
 
-		public void insertModifier(int index, InputModifier modifier) {
+		public void insertModifier(int index, Modifier modifier) {
 			if (this.tool != null) finishTracks();
 			modifiers.Insert(index, modifier);
 			modifier.activate();
 		}
-		public void addModifier(InputModifier modifier)
+		public void addModifier(Modifier modifier)
 			{ insertModifier(getModifiersCount(), modifier); }
 		
 		public void removeModifier(int index) {
@@ -293,7 +315,7 @@ namespace Assistance {
 			modifiers[i].deactivate();
 			modifiers.RemoveAt(index);
 		}
-		public void removeModifier(InputModifier modifier) {
+		public void removeModifier(Modifier modifier) {
 			for(int i = 0; i < getModifiersCount(); ++i)
 				if (getModifier(i) == modifier)
 					{ removeModifier(i); break; }
diff --git a/mono/Assistance/InputModifier.cs b/mono/Assistance/InputModifier.cs
deleted file mode 100644
index 23b1f56..0000000
--- a/mono/Assistance/InputModifier.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Assistance {
-	public interface InputModifier {
-		void activate();
-		List<Track> modify(List<Track> tracks);
-		void deactivate();
-	}
-}
-
diff --git a/mono/Assistance/InputModifierInterpolation.cs b/mono/Assistance/InputModifierInterpolation.cs
new file mode 100644
index 0000000..f526038
--- /dev/null
+++ b/mono/Assistance/InputModifierInterpolation.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+
+namespace Assistance {
+	public class InputModifierInterpolation: InputManager.Modifier {
+		public readonly double precision;
+		public readonly double precisionSqr;
+		
+		public InputModifierInterpolation(double precision = 0.0) {
+			this.precision = precision;
+			this.precisionSqr = precision*precision;
+		}
+	
+		public void addSegment(Track track, Track.WayPoint p0, Track.WayPoint p1) {
+			if ((p1.point.position - p0.point.position).lenSqr() <= precisionSqr)
+				{ track.points.Add(p1); return; }
+			Track.WayPoint p = track.modifier.calcWayPoint(0.5*(p0.originalIndex + p1.originalIndex));
+			addSegment(track, p0, p);
+			addSegment(track, p, p1);
+		}
+	
+		public override List<Track> modify(Track track, InputManager.KeyPoint keyPoint, List<Track> outTracks) {
+			if (track.handler == null) {
+				track.handler = new Track.Handler(this, track);
+				track.handler.tracks.Add(new Track( new Track.Modifier(track.handler) ));
+			}
+			
+			Track subTrack = track.handler.tracks[0];
+			Modifier modifier = (Modifier)subTrack.modifier;
+			outTracks.Add(subTrack);
+			
+			if (!track.isChanged)
+				return;
+			
+			// remove points
+			int start = track.points.Count - track.wayPointsAdded;
+			if (start < 0) start = 0;
+			int subStart = subTrack.floorIndex(subTrack.indexByOriginalIndex(start));
+			if (subTrack.points.Count < subStart) {
+				subTrack.points.RemoveRange(start, subTrack.points.Count - subStart);
+				subTrack.wayPointsRemoved += subTrack.points.Count - subStart;
+			}
+			
+			// add points
+			Track.WayPoint p0 = track.getWayPoint(start - 1);
+			for(int i = start; i < track.points.Count; ++i) {
+				Track.WayPoint p1 = track.points[i];
+				addSegment(subTrack, p0, p1);
+				p0 = p1;
+			}
+			subTrack.wayPointsAdded += subTrack.points.Count - subStart;
+		}
+	}
+}
+
diff --git a/mono/Assistance/InputModifierTangents.cs b/mono/Assistance/InputModifierTangents.cs
new file mode 100644
index 0000000..dba5aaf
--- /dev/null
+++ b/mono/Assistance/InputModifierTangents.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+
+namespace Assistance {
+	public class InputModifierTangents: InputManager.Modifier {
+		public class Modifier: Track.Modifier {
+			public Modifier(Handler handler):
+				base(handler) { }
+			
+			public InputManager.KeyPoint.Holder holder = null;
+			public readonly List<Track.Point> tangents = new List<Track.Point>();
+			
+			public override Track.WayPoint calcWayPoint(double originalIndex) {
+				double frac;
+				int i0 = original.floorIndex(originalIndex, out frac);
+				int i1 = original.ceilIndex(originalIndex);
+				Track.WayPoint p0 = original.getWayPoint(i0);
+				Track.WayPoint p1 = original.getWayPoint(i1);
+				p0.tangent = tangents[i0];
+				p1.tangent = tangents[i1];
+				return Track.interpolate(p0, p1, frac);
+			}
+		}
+
+		public override List<Track> modify(Track track, InputManager.KeyPoint keyPoint, List<Track> outTracks) {
+			if (track.handler == null) {
+				track.handler = new Track.Handler(this, track);
+				track.handler.tracks.Add(new Track( new Modifier(track.handler) ));
+			}
+			
+			Track subTrack = track.handler.tracks[0];
+			Modifier modifier = (Modifier)subTrack.modifier;
+			outTracks.Add(subTrack);
+			
+			if ( !track.isChanged
+			  && track.points.Count == subTrack.points.Count
+			  && track.points.Count == modifier.tangents.Count )
+			  	return;
+			
+			if (!track.isChanged && subTrack.points.Count == track.points.Count - 1) {
+				// add temporary point
+				modifier.tangents.Add(new Track.Point());
+				subTrack.points.Add(track.getLast());
+				++subTrack.wayPointsAdded;
+			} else {
+				// apply permanent changes
+				
+				// remove points
+				int start = track.points.Count - track.wayPointsAdded;
+				if (start < 0) start = 0;
+				if (start > 1) --start;
+				if (subTrack.points.Count < start) {
+					subTrack.points.RemoveRange(start, subTrack.points.Count - start);
+					subTrack.wayPointsRemoved += subTrack.points.Count - start;
+				}
+				if (modifier.tangents.Count < start)
+					modifier.tangents.RemoveRange(start, modifier.tangents.Count - start);
+				
+				// add first point
+				int index = start;
+				if (index == 0) {
+					modifier.tangents.Add(new Track.Point());
+					subTrack.points.Add(track.getLast());
+					++index;
+				}
+				
+				// add points with tangents
+				if (track.points.Count > 2) {
+					while(index < track.points.Count - 1) {
+						Track.WayPoint p = track.points[index];
+						double t0 = track.points[index-1].time;
+						double t2 = track.points[index+1].time;
+						double dt = t2 - t0;
+						p.tangent = dt > Geometry.precision
+						          ? (track.points[index+1].point - track.points[index-1].point)*(p.time - t0)/dt
+						          : new Track.Point();
+						modifier.tangents.Add(p.tangent);
+						subTrack.points.Add(p);
+						++index;
+					}
+				}
+				
+				track.wayPointsRemoved = 0;
+				track.wayPointsAdded = 0;
+				subTrack.wayPointsAdded += index - start;
+				
+				// release previous key point
+				if (modifier.holder) modifier.holder.Dispose();
+				
+				if (track.isFinished) {
+					// finish
+					modifier.tangents.Add(new Track.Point());
+					subTrack.points.Add(track.getLast());
+					++subTrack.wayPointsAdded;
+				} else {
+					// save key point
+					modifier.holder = keyPoint.hold();
+				}
+			}
+		}
+	}
+}
+
diff --git a/mono/Assistance/Track.cs b/mono/Assistance/Track.cs
index ef36955..79dea64 100644
--- a/mono/Assistance/Track.cs
+++ b/mono/Assistance/Track.cs
@@ -26,7 +26,7 @@ namespace Assistance {
 				{ get { return handler.owner; } }
 			public Track original
 				{ get { return handler.original; } }
-			public TrackPoint calcWayPoint(double originalIndex)
+			public virtual WayPoint calcWayPoint(double originalIndex)
 				{ return original.calcWayPoint(originalIndex); }
 		}
 
@@ -137,6 +137,9 @@ namespace Assistance {
 			{ get { return modifier != null ? modifier.original : null; } }
 		public double timeOffset
 			{ get { return modifier != null ? modifier.timeOffset : 0.0; } }
+			
+		public bool isChanged
+			{ get { return wayPointsAdded != 0 || wayPointsRemoved != 0; } }
 
 		public Track getRoot()
 			{ return original == null ? this : original.getRoot(); }