Pixel_Waves Node
Last week a Nodebox user, Ari, asked how to make wavy line art. His example showed a photograph formed by sine waves that stretched out to render light pixels and scrunched up to render dark pixels.
I provided a quick and dirty solution which did a reasonable job of this, but then became more interested and decided to do the job right and make a more robust and flexible node.
Pixel_waves takes three inputs:
- Pixels. A list of shapes that can serve as pixels to convey size, position, and brightness. This is normally an array of colored squares, like the output of my image node, but in theory it could be most any set of shapes.
- Wave Type. The choices are Sine, Square, Sawtooth, or Triangle.
- Break on White. Check if you want to treat white shapes as blank space and use that space to break rows of waves into separate paths. If unchecked, white pixels will be rendered as a slight undulation to create a very light pixel.
The node returns waves of various periods joined together into one wavy path per row (or between breaks if the break option is checked).
The attached demo shows two use cases.
The first case shows a photo (of me in Amsterdam). The output of my image node is ungrouped into separate pixels and fed directly into the pixel_waves node. Since the image was set to a relatively high resolution, it may take Nodebox 30 seconds or more to complete the render. When experimenting with photos I recommend starting with the low resolution settings while you experiment with the various image and wave options.
In the second screenshot, the word "Pixels" is fed into my pixelate node to create a pixelated word. Those pixels are then ungrouped, fed into pixel_waves, and re-colored to show the segmentation of wave paths around blank spaces. The pixel_waves node attempts to choose an appropriate stroke width for the waves (relative to pixel size); if you wish you can darken or lighten the waves by increasing or decreasing storkeWidth.
I'm still not sure how useful this node will be. I would think it might be useful to the NodeBox plotting community, since it makes phtographs easy to efficiently draw on a plotter. I hope some Nodebox plotters out there will try this node and give me some feedback.
You should also be able to produce some interesting effects. I have already made several animations. To show off the wave effect you may want to scale your image to use larger pixel sizes and strokeWidths. I notice that PDFs of high res photos did not print well, perhaps because Nodebox has problems rendering PDFs with fractional stroke widths. PNGs, if scaled up, seem to do better for printing.
I have not yet played much with other wave types or more unusual pixel shapes. As always I would be excited to see other people's experiments.
Note: I'm not sure if I should keep adding nodes like this to my library, which is already intimidatingly large. Whether or not I do may depend on how much feedback I receive about this node.
-
pixel_waves_photo_screenshot.png
2.65 MB
-
pixel_waves_pixels_screenshot.png
880 KB
- pixel_waves_node.zip 409 KB
Keyboard shortcuts
Generic
| ? | Show this help |
|---|---|
| ESC | Blurs the current field |
Comment Form
| r | Focus the comment reply box |
|---|---|
| ^ + ↩ | Submit the comment |
You can use Command ⌘ instead of Control ^ on Mac
Support Staff 1 Posted by john on 22 Jan, 2026 08:36 AM
Attached is a revised version of pixel_waves. This version uses the improved waveform node, described here: http://support.nodebox.net/discussions/show-your-work/1025-waveform...
Unlike the original version, it does not take a block of pixels. Instead, it requires one or more continuous sets of pixels, created by applying one or more path masks to the image node.
If you feed it a list of pixels produced by a single path, like a spiral or zigzag line pattern, it will return a single path consisting of joined waves along that path. If you feed it a series of pixel groups produced by multiple paths, like concentric circles or an array of line segments, it will return multiple wave paths, one for each input path.
Because it always returns a single path for each generating path, there is no longer a "Break on White" option. Light sections of the image will always show at least a minimum through line.
Pixel_waves now takes four parameters:
For best results you will want to find the best mask path(s) and image settings, then choose a wave type and adjust the amplification factor to tighten any gaps or reduce overlapping sections. The attached demo shows four different examples to demonstrate various ways the mask path and other settings can work together:
THE FIRST SCREENSHOT shows a comparison between concentric circles and a single spiral applied to a photo Floris supplied. The concentric pattern applies square waves to 42 separate circles, resulting in 42 separate wave paths, with an amplification of 90% to reduce overlaps. Notice that we had to check the close path option since circles are closed paths; if left unchecked a gap would appear at the end of each circle.
The spiral had to be resampled before being used as a mask to ensure consistent segment lengths that land evenly across the image. Here the segment length was 9, creating a fairly dense array of pixels; a longer segment length would produce a sparser spiral of pixels. Amplification was set to 300% to close the space between arms of the spiral. Sine waves were used and the close path option was unchecked since the spiral is an open path; checking that option would create an unsightly additional line connecting the end of the spiral back to its beginning.
(I included Floris's papersize node to show how this image might be prepared for final plotting. I had to set strokeWidth to .3 to avoid a too-dark result; this is probably thinner than the actual pen used by the plotter, an unresolved issue Floris and I are still working on.)
THE SECOND AND THIRD SCREENSHOTS show Floris's original image and three variations on ways to completely fill the image. Upper left is the original image. Upper right is a mask formed by a single line zig-zagging back and forth across the image; here the cell width and height of the alt_grid node control how finely the pixels are spaced. This variant uses triangle waves at 60% amplification.
Lower left uses a single Hilbert space-filling curve to create a maze-like pattern. It uses triangle waves set to 80% amplification. Lower right uses an array of slightly tilted lines to create 96 separate wave paths. Notice we had to resample these lines after clicking them to ensure fairly high rez pixels across the image (instead of pixels only on the ends of each line). This variant uses sawtooth waves at 200% amplification.
The third screenshot shows a tighter shot of the four images so that you see what the wave patterns look like up close.
THE FOURTH SCREENSHOT shows a photo of me with the same array of resamples diagonal lines used above. This time I use square waves at an amplification of 200%.
THE FIFTH SCREENSHOT shows a cat using concentric circles, again with Close Path checked. This variant uses sine waves at 100% amplification.
The zipped demo folder contains all the images and external code modules needed for these examples. To get a feel for how to achieve best results, try playing with all the settings in the demo. Switch wave types and adjust amplification. Try it on your own photos. Develop new mask paths.
This revised version of pixel_waves is flexible and powerful, but still may not produce good results for plotting. I hope Floris or other Nodebox plotters will continue to help me improve this node.