Block and Subframes/Substeps
824 9 1-
- Gaalvk
- Member
- 95 posts
- Joined: March 2025
- Offline
To create an echo/fading trail, it makes sense to use the Block solver in simulated mode and blend it with the original. But frankly, I missed the information, and it was an unpleasant discovery for me: Block ignores subframes and only works with full frames, so we'll get strobing or need to set the frame rate tenfold, which is difficult to redo once the animation is set up in frames.
How do other specialists solve this problem? How do they get smooth trails with small pitch? With a separate file and cache? Or is there some trick?
Will there be an improvement for Block in H22? Substeps, for example?
How do other specialists solve this problem? How do they get smooth trails with small pitch? With a separate file and cache? Or is there some trick?
Will there be an improvement for Block in H22? Substeps, for example?
-
- jlait
- Staff
- 7132 posts
- Joined: July 2005
- Offline
-
- jlait
- Staff
- 7132 posts
- Joined: July 2005
- Offline
-
- Gaalvk
- Member
- 95 posts
- Joined: March 2025
- Offline
Thanks for the iteration variables in H22! That helped a lot. I partially solved the problem by using $F+@iter/@count in transform formulas. This is for a simple single formula animation. A slightly more complex option for a non-formula animation is to copy from the animation outside the block chf("...", $F+@iter/@count ) or cht(..) with $T+@iter/$FPS/10. But remember, this is for a single controlled parameter. If the layer is complex with several animated elements, this won't help. I was hoping for timeshift and timepack here, but frankly, I haven't been able to use them in the block yet. Either they don't work, or I don't understand the concept. As soon as I plug in the Time Pack cable, everything stops working.
-
- jlait
- Staff
- 7132 posts
- Joined: July 2005
- Offline
Sorry, I was unclear, but the timepack goes directly to an average without using a block. The average will then do accumulation.
You can put a timepack into a block, but you need to a bit of hoop-jumping to run-over the images.
The attached shows both approaches. The block requires a cable rename to erase any incoming cable names. Then Cable Unpack can refer to them by raw index, and we can use @cop_iteration to run over the instances.
The downside of this setup is you end up pre-cooking the 40 substeps in this case, rather than doing them just in time. So this is a workaround rather than replacement for proper substepping.
You can put a timepack into a block, but you need to a bit of hoop-jumping to run-over the images.
The attached shows both approaches. The block requires a cable rename to erase any incoming cable names. Then Cable Unpack can refer to them by raw index, and we can use @cop_iteration to run over the instances.
The downside of this setup is you end up pre-cooking the 40 substeps in this case, rather than doing them just in time. So this is a workaround rather than replacement for proper substepping.
-
- omarz
- Staff
- 216 posts
- Joined: Sept. 2017
- Offline
Gaalvk
Thanks for the iteration variables in H22! That helped a lot. I partially solved the problem by using $F+@iter/@count in transform formulas. This is for a simple single formula animation. A slightly more complex option for a non-formula animation is to copy from the animation outside the block chf("...", $F+@iter/@count ) or cht(..) with $T+@iter/$FPS/10. But remember, this is for a single controlled parameter. If the layer is complex with several animated elements, this won't help. I was hoping for timeshift and timepack here, but frankly, I haven't been able to use them in the block yet. Either they don't work, or I don't understand the concept. As soon as I plug in the Time Pack cable, everything stops working.
Feeding a Time Pack cable should work if you set that input of the block to be Cable type. Inside the block, you can then use Cable Split or Cable Unpack to extract the frame of interest using the
@itervariable. The attached scene file shows this.Unfortunately, we can't yet use Time Shift to change when the input of a block should cook...
Edit: looks like Jeff and I made two versions of the same example for you! Renaming every wire to the same thing, then simply relying on the Index of Cable Unpack is a good way to generically loop over every field of a cable. The approach I'm using is stitching the name using a backticked expression, which is a little less elegant.
Edited by omarz - July 21, 2026 09:18:00
-
- Gaalvk
- Member
- 95 posts
- Joined: March 2025
- Offline
omarzYes, thanks for clarifying. That was exactly the problem. I was inserting a cable with mono layers into a regular mono input, and everything seemed to work. The cable was being transferred to the block, with all the layers inside (you can view them in the info panel inside the block). I could cut a layer, and it worked in all nodes. However, at the end, the block end returned an unexpected, incomprehensible error and a black layer, even though the block end contained a regular working layer that could be viewed. And no matter what I did, the output from the block end was empty. I simply needed to change the input type in the begin block to cable. I didn't think of that. Users, please pay attention to this: the block input type must strictly be cable!
Feeding a Time Pack cable should work if you set that input of the block to be Cable type.
-
- Gaalvk
- Member
- 95 posts
- Joined: March 2025
- Offline
To be honest, the "obvious" trick with a single field in cable unpack and a variable in the index field wasn't obvious to me at all, and I was struggling with cable split
. Thanks for pointing that out, guys!
So, once I figured out how to properly pass layers into the block and select them, I did some testing, and here is the result. In theory, this is exactly what's needed for animation with future/past sampling, just like we're used to in After Effects.
But... a time pack sampling just 10 steps from a video file immediately drops us to 5 FPS, and down to 2 FPS after passing it into the block. Selecting 1 second (24 layers) results in less than 1 FPS in the viewport.
For comparison, on the exact same video, AE gives full real-time playback when sampling 24 "future" layers using echo or time displacement, and 10-15 FPS with 48 layers. There are clearly some fundamental bottlenecks here; time pack obviously doesn't play nice with the file node (I highly dislike it too and have been complaining about its lag since Copernicus was released. Damn, how does it even manage to lag this hard?). This means my original idea — to bake the animation into a cache, load it, and apply a time shift — died instantly, which is a shame.
A simple text animation gives us 5 FPS with 100 layers and about the same with 300 layers in After Effects; a 3x difference is acceptable in theory. But the file node, with its catastrophic, tenfold drop in FPS, is something I absolutely detest.
And yeah, thanks again for your work! In any case, these nodes have opened up some interesting new possibilities for us.
. Thanks for pointing that out, guys! So, once I figured out how to properly pass layers into the block and select them, I did some testing, and here is the result. In theory, this is exactly what's needed for animation with future/past sampling, just like we're used to in After Effects.
But... a time pack sampling just 10 steps from a video file immediately drops us to 5 FPS, and down to 2 FPS after passing it into the block. Selecting 1 second (24 layers) results in less than 1 FPS in the viewport.
For comparison, on the exact same video, AE gives full real-time playback when sampling 24 "future" layers using echo or time displacement, and 10-15 FPS with 48 layers. There are clearly some fundamental bottlenecks here; time pack obviously doesn't play nice with the file node (I highly dislike it too and have been complaining about its lag since Copernicus was released. Damn, how does it even manage to lag this hard?). This means my original idea — to bake the animation into a cache, load it, and apply a time shift — died instantly, which is a shame.
A simple text animation gives us 5 FPS with 100 layers and about the same with 300 layers in After Effects; a 3x difference is acceptable in theory. But the file node, with its catastrophic, tenfold drop in FPS, is something I absolutely detest.
And yeah, thanks again for your work! In any case, these nodes have opened up some interesting new possibilities for us.
-
- Gaalvk
- Member
- 95 posts
- Joined: March 2025
- Offline
After playing around with it a bit more, I’d like to point out a few strange bottlenecks.
To make things easier to debug, I simplified my test case as much as possible: it just scales a shape and samples it over time using a noise mask. Here is what I found:
1) TimePack vs. Block.
When animating the scale and creating a TimePack with 60 layers (0-6 frames, 0.1 step), the performance hits 21 FPS. This is a very minor drop. However, if I pass this into a block with 60 iterations that samples 1 layer and sums it via a mask (basically blending all 60 layers sequentially), performance plummets to 4.5 FPS! Since it's just selecting an already cached layer, you would expect a negligible performance hit, but the drop is massive.
2) Unused Cable Overhead.
I built an empty block with 60 iterations that just loops 1 RGB layer. I added a second input for a cable that isn't used anywhere inside the block. Logically, nothing should happen. But as soon as I plug in the cable coming from the TimePack (which runs at 21 FPS on its own), the block's output drops to 11 FPS. Where did those 10 FPS go in a completely empty block? Disconnecting the cable restores the FPS back to maximum, as it should be. It feels like passing a cable into a block triggers some sort of recursive behavior or a nested "loop within a loop" under the hood.
3) Calculation vs. Sampling.
For comparison, I made a similar block with 60 iterations, but placed an animated Transform node inside it, sampling it via $F+@iter/@count. In this setup, I’m not sampling pre-cached layers; I am calculating them from scratch on every single iteration. Surprisingly, this gives me 17 FPS. Logically, calculating on the fly should be the slowest method, while sampling a pre-baked layer should be the fastest. Instead, it’s completely backwards. There seems to be a fundamental issue with how cables handle data inside blocks, making them highly inefficient right now.
4) Cable Rename Bottleneck.
Placing a CableRename node right after the TimePack immediately kills 7 FPS just to rename a single item. Dropping 7 FPS to rename 60 strings makes no sense. It’s better to avoid this node entirely. If you use a Null node before timepack and just change the output name, the layers get renamed with zero performance cost. You can then fetch layers by name using a pattern like l.@iter``. With this approach, the Cable Unpack node performs slightly better (about 0.5 FPS faster in my case) than Cable Split.
For now, the most viable and fastest workaround for simple animations seems to be sampling layers via chf() and block variables wherever possible. The performance cost of using a TimePack cable is just prohibitively expensive at the moment. Hopefully, the developers can optimize cable behavior in future updates.
To make things easier to debug, I simplified my test case as much as possible: it just scales a shape and samples it over time using a noise mask. Here is what I found:
1) TimePack vs. Block.
When animating the scale and creating a TimePack with 60 layers (0-6 frames, 0.1 step), the performance hits 21 FPS. This is a very minor drop. However, if I pass this into a block with 60 iterations that samples 1 layer and sums it via a mask (basically blending all 60 layers sequentially), performance plummets to 4.5 FPS! Since it's just selecting an already cached layer, you would expect a negligible performance hit, but the drop is massive.
2) Unused Cable Overhead.
I built an empty block with 60 iterations that just loops 1 RGB layer. I added a second input for a cable that isn't used anywhere inside the block. Logically, nothing should happen. But as soon as I plug in the cable coming from the TimePack (which runs at 21 FPS on its own), the block's output drops to 11 FPS. Where did those 10 FPS go in a completely empty block? Disconnecting the cable restores the FPS back to maximum, as it should be. It feels like passing a cable into a block triggers some sort of recursive behavior or a nested "loop within a loop" under the hood.
3) Calculation vs. Sampling.
For comparison, I made a similar block with 60 iterations, but placed an animated Transform node inside it, sampling it via $F+@iter/@count. In this setup, I’m not sampling pre-cached layers; I am calculating them from scratch on every single iteration. Surprisingly, this gives me 17 FPS. Logically, calculating on the fly should be the slowest method, while sampling a pre-baked layer should be the fastest. Instead, it’s completely backwards. There seems to be a fundamental issue with how cables handle data inside blocks, making them highly inefficient right now.
4) Cable Rename Bottleneck.
Placing a CableRename node right after the TimePack immediately kills 7 FPS just to rename a single item. Dropping 7 FPS to rename 60 strings makes no sense. It’s better to avoid this node entirely. If you use a Null node before timepack and just change the output name, the layers get renamed with zero performance cost. You can then fetch layers by name using a pattern like l.@iter``. With this approach, the Cable Unpack node performs slightly better (about 0.5 FPS faster in my case) than Cable Split.
For now, the most viable and fastest workaround for simple animations seems to be sampling layers via chf() and block variables wherever possible. The performance cost of using a TimePack cable is just prohibitively expensive at the moment. Hopefully, the developers can optimize cable behavior in future updates.
Edited by Gaalvk - July 27, 2026 18:39:10
-
- Benjamin Lemoine
- Member
- 152 posts
- Joined: Oct. 2015
- Offline
Hello I was testing this scene and noticed the FPS drop too
after a performance test i noticed the "guilty of slowing down" node was the File node
it's logical as the timepack has to load each frame before packing so...
I found that putting a cache node before the timepack helped to restore a good FPS
(i cached the amount of frame needed for the time pack)
also it's even a bit faster if you do the block calculation in an invoke node (but you have to use the iteration value from the invoke node and put the block iteration to be 1)

if it can help ✌️
after a performance test i noticed the "guilty of slowing down" node was the File node
it's logical as the timepack has to load each frame before packing so...
I found that putting a cache node before the timepack helped to restore a good FPS
(i cached the amount of frame needed for the time pack)
also it's even a bit faster if you do the block calculation in an invoke node (but you have to use the iteration value from the invoke node and put the block iteration to be 1)
if it can help ✌️
Edited by Benjamin Lemoine - July 28, 2026 11:46:27
-
- Quick Links



