Inspired by https://mapstodon.space/@bogind/109558968869019746 and listening to https://www.youtube.com/watch?v=AdyAF9M3XVw on repeat thanks to https://ruby.social/@halfbyte/109558803178815394 I made:
This was the final expression (with lots of opportunity to improve):
with_variable(
'point_at_top_of_canvas',
densify_by_count(
make_line(
point_n( @map_extent, 3), -- no idea if these indexes are stable
point_n( @map_extent, 4)
),
42 -- number of trajectories
),
collect_geometries(
array_foreach(
generate_series(1, num_points(@point_at_top_of_canvas)),
with_variable(
'point_n_of_top_line',
point_n(@point_at_top_of_canvas, @element),
point_n(
wave_randomized(
make_line(
@point_n_of_top_line,
-- make it at least touch the bottom of the canvas:
translate(@point_n_of_top_line, 0, -@map_extent_height)
),
-- fairly stupid frequency and wavelength but hey, works in any crs
1, @map_extent_width/5,
1, @map_extent_width/100,
seed:=@element -- stable waves \o/
),
floor(epoch(now())%10000/50) -- TODO make it loop around according to num_points of each line
)
)
)
)
)
Use it on an empty polygon layer with an inverted polygon style and set it to refresh at a high interval (0.01s?). Or use this QGIS project (I included some intermediate steps of the style as layer styles if you want to learn about this kind of stuff):