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

inert preview
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