diff --git a/.github/ProjectRoot/vpm-manifest-2022.json b/.github/ProjectRoot/vpm-manifest-2022.json
index 9e7156ec..fd2822c3 100644
--- a/.github/ProjectRoot/vpm-manifest-2022.json
+++ b/.github/ProjectRoot/vpm-manifest-2022.json
@@ -19,7 +19,7 @@
       "dependencies": {}
     },
     "nadena.dev.ndmf": {
-      "version": "1.5.0-alpha.2"
+      "version": "1.5.0-alpha.3"
     }
   }
 }
\ No newline at end of file
diff --git a/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs b/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs
index f3a08032..5177a872 100644
--- a/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs
+++ b/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs
@@ -37,37 +37,30 @@ private static GameObject FindAvatarRootObserving(ComputeContext ctx, GameObject
             return null;
         }
 
-        public ReactiveValue<ImmutableList<RenderGroup>> TargetGroups { get; } =
-            ReactiveValue<ImmutableList<RenderGroup>>.Create(
-            "Scale Adjuster: Find targets",
-            async ctx =>
+        public ImmutableList<RenderGroup> GetTargetGroups(ComputeContext ctx)
+        {
+            var scaleAdjusters = ctx.GetComponentsByType<ModularAvatarScaleAdjuster>();
+
+            var result = ImmutableList.CreateBuilder<RenderGroup>();
+
+            foreach (var adjuster in scaleAdjusters)
             {
-                var scaleAdjusters = await ctx.Observe(CommonQueries.GetComponentsByType<ModularAvatarScaleAdjuster>());
+                if (adjuster == null) continue;
 
-                ImmutableList<RenderGroup>.Builder result = ImmutableList.CreateBuilder<RenderGroup>();
-                
-                foreach (var adjuster in scaleAdjusters)
-                {
-                    if (adjuster == null) continue;
-                    
-                    // Find parent object
-                    // TODO: Reactive helper
-                    var root = FindAvatarRootObserving(ctx, adjuster.gameObject);
-                    if (root == null) continue;
-
-                    var renderers = ctx.GetComponentsInChildren<Renderer>(root, true);
-
-                    foreach (var renderer in renderers)
-                    {
-                        if (renderer is SkinnedMeshRenderer smr)
-                        {
-                            result.Add(RenderGroup.For(renderer));
-                        }
-                    }
-                }
-
-                return result.ToImmutable();
-            });
+                // Find parent object
+                // TODO: Reactive helper
+                var root = FindAvatarRootObserving(ctx, adjuster.gameObject);
+                if (root == null) continue;
+
+                var renderers = ctx.GetComponentsInChildren<Renderer>(root, true);
+
+                foreach (var renderer in renderers)
+                    if (renderer is SkinnedMeshRenderer smr)
+                        result.Add(RenderGroup.For(renderer));
+            }
+
+            return result.ToImmutable();
+        }
 
         public Task<IRenderFilterNode> Instantiate(RenderGroup group, IEnumerable<(Renderer, Renderer)> proxyPairs,
             ComputeContext context)
diff --git a/Editor/ShapeChanger/ShapeChangerPreview.cs b/Editor/ShapeChanger/ShapeChangerPreview.cs
index 5932705e..c737439d 100644
--- a/Editor/ShapeChanger/ShapeChangerPreview.cs
+++ b/Editor/ShapeChanger/ShapeChangerPreview.cs
@@ -17,40 +17,38 @@ namespace nadena.dev.modular_avatar.core.editor
 {
     public class ShapeChangerPreview : IRenderFilter
     {
-        public ReactiveValue<ImmutableList<RenderGroup>> TargetGroups { get; }
-            = ReactiveValue<ImmutableList<RenderGroup>>.Create(
-                "ShapeChangerPreview.TargetGroups", async ctx =>
-                {
-                    var allChangers =
-                        await ctx.Observe(CommonQueries.GetComponentsByType<ModularAvatarShapeChanger>());
+        public ImmutableList<RenderGroup> GetTargetGroups(ComputeContext ctx)
+        {
+            var allChangers = ctx.GetComponentsByType<ModularAvatarShapeChanger>();
 
-                    Dictionary<Renderer, ImmutableList<ModularAvatarShapeChanger>.Builder> groups =
-                        new Dictionary<Renderer, ImmutableList<ModularAvatarShapeChanger>.Builder>(
-                            new ObjectIdentityComparer<Renderer>());
+            var groups =
+                new Dictionary<Renderer, ImmutableList<ModularAvatarShapeChanger>.Builder>(
+                    new ObjectIdentityComparer<Renderer>());
 
-                    foreach (var changer in allChangers)
-                    {
-                        // TODO: observe avatar root
-                        ctx.Observe(changer);
-                        if (!ctx.ActiveAndEnabled(changer)) continue;
+            foreach (var changer in allChangers)
+            {
+                if (changer == null) continue;
 
-                        var target = ctx.Observe(changer.targetRenderer.Get(changer));
-                        var renderer = ctx.GetComponent<SkinnedMeshRenderer>(target);
+                // TODO: observe avatar root
+                if (!ctx.ActiveAndEnabled(changer)) continue;
 
-                        if (renderer == null) continue;
+                var target = ctx.Observe(changer.targetRenderer.Get(changer));
+                var renderer = ctx.GetComponent<SkinnedMeshRenderer>(target);
 
-                        if (!groups.TryGetValue(renderer, out var group))
-                        {
-                            group = ImmutableList.CreateBuilder<ModularAvatarShapeChanger>();
-                            groups[renderer] = group;
-                        }
+                if (renderer == null) continue;
 
-                        group.Add(changer);
-                    }
+                if (!groups.TryGetValue(renderer, out var group))
+                {
+                    group = ImmutableList.CreateBuilder<ModularAvatarShapeChanger>();
+                    groups[renderer] = group;
+                }
 
-                    return groups.Select(g => RenderGroup.For(g.Key).WithData(g.Value.ToImmutable()))
-                        .ToImmutableList();
-                });
+                group.Add(changer);
+            }
+            
+            return groups.Select(g => RenderGroup.For(g.Key).WithData(g.Value.ToImmutable()))
+                .ToImmutableList();
+        }
 
         public async Task<IRenderFilterNode> Instantiate(
             RenderGroup group,
@@ -104,15 +102,24 @@ private HashSet<int> GetToDeleteSet(SkinnedMeshRenderer proxy, ComputeContext co
                 _changers = _group.GetData<ImmutableList<ModularAvatarShapeChanger>>();
 
                 var toDelete = new HashSet<int>();
-                var mesh = context.Observe(proxy.sharedMesh);
+                var mesh = context.Observe(proxy, p => p.sharedMesh, (a, b) =>
+                {
+                    if (a != b)
+                    {
+                        Debug.Log($"mesh changed {a.GetInstanceID()} -> {b.GetInstanceID()}");
+                        return false;
+                    }
+
+                    return true;
+                });
 
                 foreach (var changer in _changers)
                 {
-                    context.Observe(changer);
+                    var shapes = context.Observe(changer, c => c.Shapes.ToImmutableList(), Enumerable.SequenceEqual);
 
                     if (!IsChangerActive(changer, context)) continue;
 
-                    foreach (var shape in changer.Shapes)
+                    foreach (var shape in shapes)
                         if (shape.ChangeType == ShapeChangeType.Delete)
                         {
                             var index = mesh.GetBlendShapeIndex(shape.ShapeName);
diff --git a/Runtime/ModularAvatarShapeChanger.cs b/Runtime/ModularAvatarShapeChanger.cs
index c4f7ffdb..0b0bb8e5 100644
--- a/Runtime/ModularAvatarShapeChanger.cs
+++ b/Runtime/ModularAvatarShapeChanger.cs
@@ -22,6 +22,26 @@ public struct ChangedShape
         public string ShapeName;
         public ShapeChangeType ChangeType;
         public float Value;
+
+        public bool Equals(ChangedShape other)
+        {
+            return ShapeName == other.ShapeName && ChangeType == other.ChangeType && Value.Equals(other.Value);
+        }
+
+        public override bool Equals(object obj)
+        {
+            return obj is ChangedShape other && Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            return HashCode.Combine(ShapeName, (int)ChangeType, Value);
+        }
+
+        public override string ToString()
+        {
+            return $"{ShapeName} {ChangeType} {Value}";
+        }
     }
 
     [AddComponentMenu("Modular Avatar/MA Shape Changer")]
diff --git a/package.json b/package.json
index e5af1a48..8a722c98 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,6 @@
   },
   "vpmDependencies": {
     "com.vrchat.avatars": ">=3.4.0",
-    "nadena.dev.ndmf": ">=1.5.0-alpha.2 <2.0.0-a"
+    "nadena.dev.ndmf": ">=1.5.0-alpha.3 <2.0.0-a"
   }
 }