Specimen / compositing
Kaleidoscope
A reusable kaleidoscope compositor. Folds any TOP (or its built-in animated source) into an N-fold mirrored mandala that rotates, twists, breathes, and tumbles. Drop it onto any visual via the External source mode.
intermediate 11 operators
Node graph preview
Kaleidoscope graph
Show raw TDN YAML
Paste in TouchDesigner with Embody running.
format: tdn
version: '2.0'
build: null
generator: Embody/6.0.15
td_build: 099.2025.32820
source_file: Embody-6.15.toe
exported_at: '2026-06-11T03:24:06Z'
network_path: /specimen_lab/kaleidoscope
options:
include_dat_content: true
include_storage: true
type_defaults:
annotateCOMP:
parameters:
order: =me.digits or 0
layerzone: =0 if hasattr(me, 'EncloseOPs') and me.EncloseOPs else 1
flags:
- display
color: [0.45, 0.45, 0.45]
glslTOP:
flags:
- viewer
size: [130, 90]
color: [0.67, 0.67, 0.67]
infoDAT:
flags:
- viewer
size: [130, 90]
color: [0.67, 0.67, 0.67]
textDAT:
parameters:
language: glsl
flags:
- viewer
size: [130, 90]
color: [0.67, 0.67, 0.67]
par_templates:
about:
- default: '1.0'
help: Annotate COMP default setup version.
name: Version
readOnly: true
startSection: true
style: Str
- help: Click to open help page.
name: Help
style: Pulse
text:
- default: Annotate
help: Text in the title bar.
label: Title Text
name: Titletext
startSection: true
style: Str
- clampMin: true
default: 30
help: Height of the title bar. Title font height adjusts automatically to fill.
label: Title Height
name: Titleheight
normMax: 100.0
normMin: 5.0
style: Int
- default: left
help: Alignment of title text.
label: Title Align
menuLabels:
- Left
- Center
- Right
menuNames:
- left
- center
- right
name: Titlealign
style: Menu
- help: Text in the body area. Use an expression for newlines etc.
label: Body Text
name: Bodytext
startSection: true
style: Str
- clampMin: true
default: 10
help: Size of text in the body area.
label: Body Font Size
name: Bodyfontsize
normMax: 100.0
normMin: 8.0
style: Int
- help: Limit the width of the text area.
label: Limit Body Text Width
name: Bodylimitwidth
style: Toggle
- clampMin: true
default: 1000
help: Width limit that will cause wraparound or cut-off. Measured in Panel units.
label: Max Body Text Width
name: Bodymaxwidth
normMax: 2000.0
normMin: 100.0
style: Int
settings:
- default: comment
help: 'Switch between Comment, Network Box, and Annotate Modes. '
menuLabels:
- Comment
- Network Box
- Annotate
menuNames:
- comment
- networkbox
- annotate
name: Mode
style: Menu
- help: Converts quotes, ellipsis, and dashes to more typographically nice unicode versions.
label: Smart Quote
name: Smartquote
startSection: true
style: Toggle
- default: true
help: Wrap body text when it extends past right bound.
label: Body Word Wrap
name: Bodywordwrap
style: Toggle
- default: 1
help: Background color base.
label: Back Color
name: Backcolorr
startSection: true
style: RGBA
- clampMax: true
clampMin: true
default: 1
help: Back color alpha.
label: Back Color Alpha
name: Backcoloralpha
style: Float
- clampMax: true
clampMin: true
default: 1
help: Opacity of the entire Annotate.
label: Annotate Opacity
name: Opacity
style: Float
op_viewer:
- help: Turn the visibility of the viewer specified in the OP parameter below on or off.
label: Viewer Display
name: Opviewerdisplay
style: Toggle
- help: The operator whose viewer is displayed in the Annotate.
label: OP
name: Opviewer
style: OP
- help: Allow interaction with the OP viewer.
label: OP Viewer Interactive
name: Opviewerinteractive
style: Toggle
- default: 'False'
help: Use the Size/Aspect Override to control viewer's size in the background.
label: Size/Aspect Override
menuLabels:
- Natural
- Specify
- Auto-Fit
menuNames:
- natural
- specify
- autofit
name: Opvieweroversize
startSection: true
style: Menu
- clampMin: true
default: 800
help: Diplay viewer as-if it were being displayed at this resolution. This is particularly useful for zooming into operators that don't have a built-in resolution, like CHOPs, SOPs, and DATs.
label: Size/Aspect
name: Opviewersize
normMax: 1000.0
normMin: 1.0
style: WH
- default: 1
help: Scale the viewer by this factor.
label: Scale
name: Opviewerscale
normMax: 2.0
startSection: true
style: Float
- help: Move the border of the viewer towards left edge of Annotate when negative or towards right edge when positive.
label: Justify X
name: Opviewerjustifyx
normMin: -1.0
style: Float
- label: Justify Y
name: Opviewerjustifyy
normMin: -1.0
style: Float
- help: When True, allow viewer to display in the Annotate title area as well as body.
label: Cover Body and Title
name: Opviewerfillbodytitle
style: Toggle
- clampMin: true
default: 1
help: Zoom the viewer by this scale factor without increasing the size of its display area in the Annotate.
label: OP Viewer Zoom
min: 0.001
name: Opviewerzoom
normMax: 5.0
normMin: 1.0
startSection: true
style: Float
- help: Offsets the displayed area within the viewer. Combined with OP Viewer Zoom, this lets you display a specific area of a viewer, such as a CHOP channel or table cell.
label: OP Viewer Offset
name: Opvieweroffsetx
style: XYZW
- clampMax: true
clampMin: true
default: 1
help: Alpha value of the background area in the OP Viewer.
label: Fill Alpha
name: Opviewerfillalpha
startSection: true
style: Float
type: baseCOMP
custom_pars:
Kaleidoscope:
- name: Segments
style: Int
startSection: true
default: 8
min: 2.0
max: 24.0
clampMin: true
normMin: 2.0
normMax: 16.0
help: Number of mirror wedges around the center. Higher = more intricate symmetry.
- name: Rotation
style: Float
normMin: -3.1416
normMax: 3.1416
help: Base rotation of the mirror, in radians. The animation rotates on top of this.
- name: Zoom
style: Float
default: 1
min: 0.1
clampMin: true
normMin: 0.3
normMax: 3.0
help: How much of the source each wedge samples. Higher = more facets packed in.
- name: Flowspeed
style: Float
label: Flow Speed
default: 1
clampMin: true
normMax: 3.0
help: Speed of the tumble / twist / rotation animation. 0 freezes it.
- name: Palette
style: Float
label: Palette Hue
clampMin: true
clampMax: true
help: Rotates the color palette through the full hue range. 0 = fiery.
- name: Source
style: Menu
startSection: true
default: demo
menuNames:
- demo
- external
menuLabels:
- Demo (built-in)
- External input
help: Demo = built-in animated source. External = kaleidoscope whatever TOP is wired into this COMP.
color: [0.67, 0.67, 0.67]
operators:
- name: in1
type: inTOP
parameters:
label: =me.name
size: [130, 90]
color: [0.67, 0.67, 0.67]
- name: out1
type: outTOP
parameters:
label: =me.name
flags:
- viewer
position: [1700, -150]
size: [130, 90]
color: [0.67, 0.67, 0.67]
inputs:
- glsl_kaleido
- name: annotate1
type: annotateCOMP
sequences:
ext:
- object: op.TDAnnotate.mod.AnnotateExt.AnnotateExt(me)
promote: true
custom_pars:
Text:
$t: text
Titletext: Input & Source
Bodytext: Chooses what gets mirrored. glsl_source is a built-in animated fBm field; in1 is this COMP's external input; switch_source picks between them via the Source parameter.
Settings:
$t: settings
Mode: annotate
Backcolorr: [0.12, 0.18, 0.24]
Opacity: 0.3
OP Viewer:
$t: op_viewer
Opvieweroversize: natural
About:
$t: about
position: [-220, -490]
size: [870, 750]
palette_clone: true
- name: annotate2
type: annotateCOMP
sequences:
ext:
- object: op.TDAnnotate.mod.AnnotateExt.AnnotateExt(me)
promote: true
custom_pars:
Text:
$t: text
Titletext: Kaleidoscope
Bodytext: Folds the source into N mirrored wedges (Segments) with rotation, a breathing zoom, an oscillating twist, and a drifting sample so the content tumbles through the symmetry. out1 exposes the result.
Settings:
$t: settings
Mode: annotate
Backcolorr: [0.2, 0.14, 0.18]
Opacity: 0.3
OP Viewer:
$t: op_viewer
Opvieweroversize: natural
About:
$t: about
position: [1080, -340]
size: [820, 450]
palette_clone: true
- name: glsl_source
type: glslTOP
parameters:
pixeldat: glsl_source_pixel
computedat: glsl_source_compute
outputresolution: custom
resolutionw: 1280
resolutionh: 1280
sequences:
vec:
- name: uPal
valuex: =parent().par.Palette.eval()
position: [0, -300]
- name: glsl_kaleido
type: glslTOP
parameters:
pixeldat: glsl_kaleido_pixel
computedat: glsl_kaleido_compute
sequences:
vec:
- name: uParams
valuex: =parent().par.Segments.eval()
valuey: =parent().par.Rotation.eval()
valuez: =parent().par.Zoom.eval()
valuew: =absTime.seconds*parent().par.Flowspeed.eval()
position: [1300, -150]
inputs:
- switch_source
- name: switch_source
type: switchTOP
parameters:
index: =parent().par.Source.menuIndex
position: [450, -150]
size: [130, 90]
color: [0.67, 0.67, 0.67]
inputs:
- glsl_source
- in1
- name: glsl_source_info
type: infoDAT
parameters:
op: glsl_source
position: [0, -420]
dock: glsl_source
dat_read_only: true
- name: glsl_kaleido_info
type: infoDAT
parameters:
op: glsl_kaleido
position: [1300, -270]
dock: glsl_kaleido
dat_read_only: true
- name: glsl_source_pixel
type: textDAT
parameters:
extension: frag
position: [150, -420]
dock: glsl_source
dat_content: |-
uniform vec4 uPal; // x = hue 0..1
out vec4 fragColor;
float hash(vec2 p){ return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453); }
float noise(vec2 p){ vec2 i=floor(p),f=fract(p); vec2 u=f*f*(3.0-2.0*f);
return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),u.x),u.y); }
float fbm(vec2 p){ float v=0.0,a=0.5; for(int i=0;i<5;i++){v+=a*noise(p);p*=2.0;a*=0.5;} return v; }
void main(){
vec2 uv=vUV.st; vec2 p=uv*3.0;
float n1=fbm(p);
float n2=fbm(p*1.8 + n1*2.0);
vec3 col = 0.5 + 0.5*cos(vec3(0.0,0.9,1.9) + n2*3.2 + uPal.x*6.2831);
col = mix(vec3(dot(col,vec3(0.299,0.587,0.114))),col,1.35);
fragColor=vec4(clamp(col,0.0,1.0),1.0);
}
dat_content_format: text
- name: glsl_kaleido_pixel
type: textDAT
parameters:
extension: frag
position: [1450, -270]
dock: glsl_kaleido
dat_content: |-
uniform vec4 uParams; // x=segs, y=rot_base, z=zoom_base, w=time
out vec4 fragColor;
const float PI=3.14159265;
void main(){
float t = uParams.w;
vec2 uv = vUV.st - 0.5;
float segs = max(2.0, uParams.x);
float rot = uParams.y + t*0.06;
float zoom = max(0.1, uParams.z) * (1.0 + 0.10*sin(t*0.20));
float r = length(uv) * zoom;
float a = atan(uv.y, uv.x) + rot;
a += 0.35 * sin(t*0.15) * r; // oscillating twist (swirl)
float seg = 2.0*PI/segs;
a = mod(a, seg);
a = abs(a - seg*0.5);
vec2 pp = vec2(cos(a), sin(a)) * r + 0.5;
pp += vec2(sin(t*0.07)*0.15 + t*0.012, // content drifts/tumbles
cos(t*0.05)*0.15 + t*0.009);
pp = abs(fract(pp*0.5)*2.0 - 1.0);
fragColor = texture(sTD2DInputs[0], pp);
}
dat_content_format: text
- name: glsl_source_compute
type: textDAT
position: [-150, -420]
dock: glsl_source
- name: glsl_kaleido_compute
type: textDAT
position: [1150, -270]
dock: glsl_kaleido
annotations:
- name: annotate1
mode: annotate
title: Input & Source
text: Chooses what gets mirrored. glsl_source is a built-in animated fBm field; in1 is this COMP's external input; switch_source picks between them via the Source parameter.
position: [-220, -490]
size: [870, 750]
color: [0.12, 0.18, 0.24]
opacity: 0.3
- name: annotate2
mode: annotate
title: Kaleidoscope
text: Folds the source into N mirrored wedges (Segments) with rotation, a breathing zoom, an oscillating twist, and a drifting sample so the content tumbles through the symmetry. out1 exposes the result.
position: [1080, -340]
size: [820, 450]
color: [0.2, 0.14, 0.18]
opacity: 0.3