From cgwiki

Dust motes expression

Basically noise, warped by noise, smoothstepped at the peaks to simulate dust/motes floating in space. Duplicate this a few times, alter the user controls to set different scales and speeds, and merge them all together (all the anim is in the alpha channel btw)


The above gif is 3 copies, mult-ed up to an intensity of 30, multed again against a section of a colour wheel, then convolved.

set cut_paste_input [stack 0]
version 8.0 v5
push $cut_paste_input
Reformat {
 format "1024 1024 0 0 1024 1024 1 square_1K"
 name Reformat2
 selected true
 xpos -54
 ypos -220
Expression {
 expr3 smoothstep(low,high,noise((x*xscale)+noise(y/warpscale+(frame*speed)),(y*yscale)+noise(x/warpscale+(frame*speed)),((frame+offset)*evolvespeed)))
 name motes
 selected true
 xpos -62
 ypos -128
 addUserKnob {20 User}
 addUserKnob {7 offset}
 offset 0.444
 addUserKnob {7 warpscale}
 warpscale 20
 addUserKnob {7 low}
 low 0.524
 addUserKnob {7 high}
 high 1
 addUserKnob {7 speed}
 speed 0.03
 addUserKnob {7 xscale}
 xscale 0.27
 addUserKnob {7 yscale}
 yscale 0.365
 addUserKnob {7 evolvespeed}
 evolvespeed 0.001

Expression to generate random number between 5 and 15 each second


Text node to display image sequence name with tcl

This will find the topmost node above the text, assume its a read node, grab the name of the image, and strip the frame number and suffix.

[lrange [split [basename [value [topnode].file]] .] 0 0 ]

Working from inside to outside:

[value [topnode].file] -- get the value of the requested knob, in this case, the 'file' value of the topmost node ( I assume its a read node)

[basename ... ] -- given a full /big/path/to/image/sequence/beauty.1234.exr, return beauty.1234.exr

[ split .... .] -- take a string, and split it by the given character. in this case, split 'beauty.1234.exr' into 'beauty' '1234' 'exr'.

[lrange ... 0 0 ] -- given a list, return elements from x to x. Here, I ask from 0 to 0, ie, just the first element in the array, so here it'd return 'beauty'.

This page has been a great cheat sheet for all things tcl and nuke:

Obligatory lazy copy/paste setup:

set cut_paste_input [stack 0]
version 8.0 v5
Read {
 inputs 0
 file /big/path/to/image/sequence/beauty.1234.exr
 format "1024 1024 0 0 1024 1024 1 square_1K"
 origset true
 on_error black
 name Read1
 selected true
 xpos -347
 ypos -159
Text2 {
 font_size_toolbar 144
 font_width_toolbar 100
 font_height_toolbar 100
 message "\[lrange \[split \[basename \[value \[topnode].file]] .] 0 0]"
 box {0 733 {width 1813 x1060 446} {height 878 x1060 878}}
 hidden_bbox {0 733 446 878}
 transforms {{0 2}
 cursor_position 6
 font_size 144
 center {1440 1080}
 cursor_initialised true
 initial_cursor_position {{0 878}
 group_animations {{0} imported: 0 selected: 0 items: "root transform/"}
 animation_layers {{1 11 1440 1080 0 0 1 1 0 0 0 0}
 name Text1
 selected true
 xpos -347
 ypos -53

if/then syntax

I always forget this. Set a switch node to 1 if the frame is between 90 and 167, otherwise 0:

(frame>90&&frame<167) ? 1 : 0


thing_to_test ? result_if_true : result_if_false

Copy paste thing if you're lazy:

set cut_paste_input [stack 0]
version 7.0 v9
push 0
push $cut_paste_input
Switch {
 inputs 2
 which {{(frame>90&&frame<167)?1:0}}
 name Switch2
 selected true
 xpos -495
 ypos -522

Get digits in the name of a node aka opdigits

Nuke opdigits.gif

Oh TCL, you fickle mistress. Thanks for the tip Lorne!

[ regexp -inline {\d+}  [knob name] ]

Nuke folk will look at that and be all like 'whaaa?', while Houdini folk will look at that and go 'Ah, opdigits, gotcha!'.

For you nuke squares, its a common thing in Houdini to have random behaviour be seeded from the name of a node. So if you have a cool noise thing in an expression, and you decide you want multiple copies of your thing, but have them all be different, as they all get named Expression1, Expression2, Expression63 etc, you can extract the 1, 2, 63, and feed that to your noise function.

Paste this into a nuke script, and keep pasting to see that each copy returns a different result:

set cut_paste_input [stack 0]
version 9.0 v9
push $cut_paste_input
Expression {
 temp_name0 seed
 temp_expr0 "\[ regexp -inline \{\\d+\}  \[knob name] ]"
 temp_name1 scale
 temp_expr1 10*random(seed*10)
 temp_name2 offset
 temp_expr2 random(seed)*2
 expr0 noise((cx+offset)*scale,(cy+offset)*scale,0)
 name Expression1
 selected true
 xpos 207
 ypos -161

Tile a uv map

Uv expression tiles.gif

Use an expression node, give it sliders for the number of repeats in x and y, and use modulus to find the remainder of (1/repeats) * repeats.

set cut_paste_input [stack 0]
version 7.0 v9
push $cut_paste_input
Expression {
 temp_name0 rate
 temp_expr0 5
 expr0 r%(1/repeatx)*repeatx
 expr1 g%(1/repeaty)*repeaty
 name Expression2
 label "multiply uvs"
 selected true
 xpos 2359
 ypos -173
 addUserKnob {20 User}
 addUserKnob {7 repeatx R 0 10}
 repeatx 1.55
 addUserKnob {7 repeaty R 0 10}
 repeaty 15

Offset uv map with wraparound

Uv expression offset.gif

I had a hacky version of this before, now less hacky. Can make it pan in any direction now, and the speed control works as you'd expect (low values = slow, high values = fast)

set cut_paste_input [stack 0]
version 8.0 v1
push $cut_paste_input
Expression {
 temp_name0 shiftx
 temp_expr0 (time/24*ratex)%1
 temp_name1 shifty
 temp_expr1 (time/24*ratey)%1
 expr0 "( r+shiftx < 0) ? 1+r+shiftx :  (r+shiftx)%1"
 expr1 "( g+shifty < 0) ? 1+g+shifty :  (g+shifty)%1"
 name Expression7
 label "offset uvs"
 selected true
 xpos -322
 ypos -22
 addUserKnob {20 User}
 addUserKnob {7 ratex t "\t\t" R -10 10}
 ratex 1.35
 addUserKnob {7 ratey R -10 10}
 ratey -0.85
 addUserKnob {7 time R -1000 1000}
 time {{frame}}

Quick dummy uv map

Uv expression square.jpg

Feeling lazy? Sure you are.

set cut_paste_input [stack 0]
version 7.0 v9
push $cut_paste_input
Expression {
 expr0 x/width
 expr1 y/height
 name Expression8
 label "square uv map"
 selected true
 xpos 2444
 ypos -601


# make a node
#assign to a variable
b = nuke.toNode('Blur1')
c = nuke.toNode('Checkerboard4')
#set a value
#connect 2 nodes
# get a list of all the attributes
[b.knob(x).name() for x in range(b.getNumKnobs()) ]
# set all write nodes to have their also_merge node to 'all'
nodes = [ n for n in nuke.allNodes() if 'Merge' in n.Class() ]
for n in nodes:
     print n['also_merge'].setValue('all')
# set all pbw nodes for the aov-reanders to premultiplied
for node in nuke.allNodes():
    if node.Class() == 'Group' and 'aov' in node['name'].getValue():
        if 'pbw' in node['passName'].getValue():
                print node['name'].getValue()
                print 'result', node['unpremultiplied'].getValue()
                print '================bad================='
                print node
# set selected reads to have error mode as 'nearest frame'
[ x['on_error'].setValue("nearest_frame") for x in nuke.selectedNodes() ]
# set knob default. in this case, make the exposure tool work in stops, and set the label to show you the stops value. similar thing for gamma nodes.
nuke.knobDefault('EXPTool.label','[value red]')
nuke.knobDefault('Gamma.label','[value value]')

Command line python

Start nuke command prompt. On linux, annoyingly, there's no cursor or backspace/delete support, can't get readlines/ncurses up n running yet.

nuke -t

Script that define functions to load a script, return a write node, and execute a write node:

import nuke
def do():
    inScript = '/depts/cg_treehouse/matthewes/mineral/gammafix.nk'
    nuke.scriptOpen( inScript )
    writes = [ n for n in nuke.allNodes() if 'Write' in n.Class() ]
    return writes[0]
def run(n):

Save that as '', call it from the nuke command line python prompt like this:

import batchnuke as bn
n =

Can make chages to the python script and reload it with


Once this is run, the script should also be available live in the interactive prompt, eg

>>> nodes = nuke.allNodes()
>>> nodes
[<Viewer1 at 0x2b09a50>, <Write1 at 0x2b09a30>, <Grade1 at 0x2b09a10>, <Saturation1 at 0x2b099f0>, <Gamma1 at 0x2b099d0>, <Read1 at 0x2b099b0>]

Batch process, full example

Here's a thing that loads a script, finds every mp4 in the current folder, and for each file creates a new read node, swaps the input on the first node after the initial read (hardcoded to a gamma node for now), updates the write path, renders with new framerange.

Had to create a new read node rather than updating in-place, nuke never updates the last frame on the existing read node for some reason.

import nuke
import os
def load():
    inScript = '/path/to/nukescript/gammafix.nk'
    nuke.scriptOpen( inScript )
    writes = [ n for n in nuke.allNodes() if 'Write' in n.Class() ]
    return writes[0]
def swap():
    files = os.listdir('/location/of/video/folder/')
    files = [f for f in files if f.endswith('.mp4')]
    for f in files:
        r = nuke.allNodes('Read')[0]
        g = nuke.allNodes('Gamma')[0]
        print 'old file = ', r['file'].value()
        print 'old range = ', r['last'].getValue()
        end = int(mov['last'].getValue())
        print 'new range = ', end
        w = nuke.allNodes('Write')[0]
        print 'old write = ', w['file'].value()
        newwrite = f.replace('thumbnail','cc')
        print 'new write = ', w['file'].value()
        if (os.path.exists(newwrite)):
                print 'skipping file', newwrite
> nuke -t
import batchnuke as bn; bn.load(); 
reload(bn); bn.swap()