Turn one still astro image into a cinematic MP4 flythrough — no video software required.
You give it between one and three images (starless is required, mid layer is optional, stars layer is optional). For each layer you configure a "camera move" — where the camera starts, where it ends, how fast it zooms, how it eases between the two. On export, the tool renders every frame at your target resolution and FPS, composites the layers together, and writes an MP4.
Because each layer can have its own zoom speed, you get parallax for free: zoom the starless layer from 1× to 6× and the stars layer from 1× to 4×, and the stars appear to drift behind the nebula as you fly through it. Add the "Nebula depth warp" effect and bright structures actually come forward on the Z axis.
| Layer | What it is | Where it sits |
|---|---|---|
| Starless (required) | Your nebula image with stars removed. Usually from GraXpert, StarXTerminator, or the SASpro Remove Stars tool. | Bottom. The base of the composite. |
| Mid (optional) | Any extra image you want blended in — a narrowband channel, a different stretch of the same data, a second nebula, text overlay, anything. | Middle. Composited over the starless layer using the Blend mode and Opacity you pick. |
| Stars (optional) | The stars-only output from your star removal step. | Top. Composited over the mid+starless result, usually using Screen so stars glow through. |
Each layer has independent zoom, center, easing, lens effects, blend mode, and opacity. The compositing order is fixed:
Each layer has an identical "Lens Effects" box with four optional effects, plus a master "Ramp effects with zoom" checkbox that controls when they kick in.
| Effect | What it does | Good for |
|---|---|---|
| Nebula depth warp | Treats image luminance as a 3D height field and shifts bright pixels toward you as you zoom. Includes separate Depth blur (smooth the heightmap) and Invert depth (dark = closer) controls. | Any nebula where the bright structures should feel "in front of" the dim backdrop. Dusty structures become visibly volumetric. |
| Radial edge stretch | Pincushion / barrel distortion. Positive values stretch the edges outward; negative values compress them inward. | Fisheye-ish immersion, exaggerating the "flying through a tunnel" sensation. |
| Zoom blur | Multi-sample streaking along the radial direction from the zoom center. | Warp-speed stars. Pair with high zoom and Ease In for a dramatic acceleration. |
| Chromatic aberration | Shifts the red channel inward and blue channel outward, each by a few percent per unit strength. | Sci-fi aesthetic, lens-realism look, "cheap telescope" feel. Subtle values (0.1-0.3) sell it; higher is comic-book. |
Twelve blend modes are available for the mid and stars layers:
| Mode | When to use |
|---|---|
| Screen | Default for the stars layer. Preserves star brightness, invisible on black backgrounds. Also good for additive glow effects on mid. |
| Add | Harder than Screen — can blow out highlights. Use for intentionally burning in a layer. |
| Average | Simple arithmetic mean. A gentle 50/50 blend. |
| Max / Lighten | Keep the brighter pixel per channel. Alternative to Screen for stars. |
| Multiply | Darken-only. Good for burning in dust lanes or shadow layers. |
| Overlay | Contrast boost — multiplies darks, screens lights. Classic "pop" blend for mid. |
| Soft Light | Gentle contrast boost. Pleasant for subtle enhancements. |
| Hard Light | Aggressive contrast boost — like Overlay but condition is on the top layer. |
| Difference | Shows where layers disagree. Useful for debugging alignment, less for aesthetics. |
| Color Dodge | Brightens based on top layer. Very intense; use sparingly. |
| Darken | Keep the darker pixel per channel. |
| Threshold Mask | Pixels brighter than Threshold sit on top of the base; dimmer pixels are transparent. Great for using stars as a cutout. Threshold and Feather sliders appear when this mode is selected. |
The Working res dropdown (in the Output panel) controls the resolution used for the live preview — SD, HD, 2K, 4K, or Full. Smaller working resolutions make the preview more responsive and depth-map calculations faster, especially for very large source images.
If PyTorch is installed and a CUDA GPU or Apple Silicon MPS device is available, rendering runs on the GPU automatically. The status line during export shows [GPU] or [CPU] per frame so you can confirm which backend is active. The pipeline silently falls back to CPU if GPU rendering fails on any frame — your render will finish either way, just slower on CPU for high resolutions.
Typical speedup from GPU: 5-15× on a modern discrete card, 3-5× on Apple Silicon. For a 10-second 1080p30 render (300 frames), that's the difference between coffee-break and an entire movie.
The easing curves, the torus-wrapped zoom crop, the depth-aware parallax, the blend-mode math, the motion-coupled effect ramp, and the GPU grid-sample pipeline.
Four easing curves map normalized time t ∈ [0, 1] to an eased t ∈ [0, 1]:
Each layer picks its own curve independently — a common technique is Ease In for the starless layer and Linear for the stars layer, which makes the nebula accelerate forward while the starfield slides past at constant speed, selling parallax more strongly.
Given a zoom factor z and center (cx, cy) in fractional image coordinates, the crop window dimensions are:
The window spans:
and samples are taken on a regular grid interpolated into an output of size (out_h, out_w). When the crop window extends beyond the image boundary — common at high zoom near the edge — the coordinates wrap modulo (W, H):
This is a torus wrap boundary mode — the image is treated as a seamlessly tileable texture. Unlike reflect or clamp modes, it never produces visible edge streaks or mirror reflections at the frame boundary, which was a visual flaw of earlier approaches. OpenCV's cv2.remap with BORDER_WRAP handles this on the CPU path; on GPU, normalized coordinates are folded with a custom modulo on [-1, 1].
With Nebula depth warp enabled, each output pixel's source location is perturbed radially based on local luminance. The pipeline:
The result is per-pixel differential zoom — the nebula's brightest arcs lean forward during the fly-in, dust lanes recede, all without a true 3D model.
All blend modes operate pixelwise, per channel, on float32 values in [0, 1]. a is the base (lower) layer, b is the layer being blended on top:
Hard Light swaps the condition in Overlay to test b < 0.5 instead of a. Max/Lighten take max(a, b); Darken takes min(a, b); Average is (a + b)/2. The Soft Light formula is the Pegtop variant — simpler and more stable than the Photoshop recipe, and visually indistinguishable for typical use.
After the blend, opacity α interpolates between base and blended:
Threshold Mask is an alpha composite rather than a pixelwise blend. Luminance of the top layer m drives a per-pixel alpha:
where τ is the Threshold slider and σ is the Feather slider. The final result is a straight over-composite:
This gives you a clean mask that fades smoothly within a σ-wide luminance band around τ. For a stars layer, setting τ ≈ 0.15 and σ ≈ 0.08 makes the stars sit cleanly on top of the base while the dark gaps between stars reveal the layers beneath.
For each pixel at offset (dx, dy) from the zoom center, with half-diagonal of the frame as the normalizing length, the normalized radius is:
and the source sample location becomes:
where k is the strength slider. The r² factor means the center is unaffected while edges distort most — standard radial distortion. Positive k gives barrel distortion (edges pushed outward); negative k gives pincushion (edges pulled inward). Range of the strength slider is [-0.5, 0.5].
Zoom blur is implemented as a multi-sample accumulation at progressive zoom levels. For N samples (default 12):
where zmax = 1 + 0.25 · s. The samples are weighted so the undistorted (i=0) sample has weight 1 and the most-zoomed sample has weight 0.4, giving a trailing look rather than a smeared one. The weighted average is blended into the original image at blend factor 0.7 · s:
Visually: stars become radial streaks pointing away from the zoom center, with the streak length controlled by the strength slider.
Red and blue channels are independently zoomed with a small offset relative to green:
Red zooms in (shifts inward toward center), blue zooms out (shifts outward toward edges). The green channel is left unchanged. This mimics the way real refractors with imperfect correction spread colors radially at the edges of the field, giving a lens-realism feel. At s = 1 (max slider), the shift is 3% — enough to see at the frame edges, invisible near center.
With "Ramp effects with zoom" enabled, each effect's strength is modulated by the current zoom velocity, not by elapsed time. For eased time function f(t), the velocity at time t is approximated by a small central difference:
normalized to a visual ramp:
with ε = 0.005. The effect strength the user set in the slider is then multiplied by ramp(t) before being applied. For an Ease In curve, v(0) ≈ 0 and v(1) is maximum — so chromatic aberration and zoom blur both ease in with the camera's acceleration. For Linear, v is constant and the effects stay pinned at their slider values. The divisor 3 is empirical: it means Ease In-Out and Linear end up at roughly the same peak effect strength despite having very different velocity curves.
The final frame is built in two steps:
When the mid layer is absent, step 1 is skipped and base = starless. When the stars layer is absent, step 2 is skipped and the final frame is base. The default stars blend is Screen with opacity 1.0, which reproduces the classic "stars-plus-nebula" composite you'd otherwise do manually.
When PyTorch is available with a CUDA or MPS backend, the zoom crop, radial stretch, zoom blur, chromatic aberration, and depth warp are all implemented via torch.nn.functional.grid_sample operating on NCHW tensors. The GPU-side torus wrap is implemented by folding normalized grid coordinates into [-1, 1]:
which is equivalent to the modulo wrap in image space. The GPU path uses padding_mode="zeros" because the wrap already keeps all coordinates inside [-1, 1], so the zero-padding fallback is never actually triggered.
Depth maps are uploaded once per render to a (1, 1, H, W) tensor, then sampled on-GPU each frame. Images are stored on-GPU as (1, 3, H, W) float32. Each layer composite is a Python function call that returns a tensor, so blend modes can chain without any CPU round-trip — only the final (1, 3, out_h, out_w) frame is pulled back to numpy and converted to BGR uint8 for the OpenCV MP4 writer.
cv2.remap with BORDER_WRAP, cv2.VideoWriter with mp4v FOURCC) for CPU warping and video output; PyTorch (grid_sample) for GPU warping with CUDA / MPS backends; the cubic easing curves (Ease In / Out / In-Out) are the standard animation-industry formulas; the Pegtop Soft Light formula is widely used as a more stable alternative to the Photoshop recipe; the Rec.709 luminance weights (0.2126, 0.7152, 0.0722) are the standard color-space constants; chromatic aberration via per-channel radial zoom is a classical lens-simulation approach.