Beginners Guide to MilkDrop Preset Writing
Authors: Rovastar (rovastar@hotmail.com) and Krash (krash@idx.com.au) Contributors: Unchained, Geiss Version: v1.6, 28 Feb 2002
Section 1: Introduction
This guide has been written by some of the more established and experienced preset authors out there to attempt to produce a step-by-step guide/tutorial to writing MilkDrop presets. It isn’t in any real order and is added to all the time.
A useful place to learn more about how existing presets work is the walkthroughs section. Krash wrote all of these and they are excellent guides for explaining what is going on. They build up slowly and are invaluable for explaining what is going on in detail.
It is a guide for the potential budding new preset authors out there who see MilkDrop and think “this is cool I want to get involved but where on earth do I start!” as well as those who have started writing and want to improve their skills.
First off, giving a black and white, step-by-step guide is unbelievably difficult because MilkDrop can do so much. And all the different variables have to be just right for it to look good. A bit like art in a way — what someone thinks is really good another may think is truly awful.
That said it is not that difficult to create an effect that you should be pleased with.
Remember to get the latest versions of MilkDrop from: http://www.nullsoft.com/free/milkdrop/
And when you have written your brilliant new preset post it to: http://forums.winamp.com/forumdisplay.php?forumid=84
Section 2: Getting Started
Four things are useful for writing presets: Mathematics knowledge, artistic flare, persistence and luck. If you have any of these you will be able to create a decent preset and the more of each of these you have the better presets will become.
And before you go any further please read Ryan’s “MilkDrop Preset Authoring Guide” found in the help. There is a lot of useful stuff in there. Print out a copy, if you can, along with this guide — it will help when editing your presets on the fly.
If you have not got a clue after reading this then maybe a mathematics textbook will be handy. If you are thinking about maths “Arrrah, let me out!!” then don’t worry you can create decent presets with very little mathematics knowledge. You will need to know what the sine (sin), cosine (cos), and (to a lesser extent) tangent (tan) and logarithms (log and log10) equations roughly do. These are used loads in the MilkDrop presets.
But to be fair the more you know the greater your potential of writing a better preset. You can just randomly put things in and possibly get a decent result, but if you actually understand what you’re doing with the mathematics, you’ll be able to get specific effects with relative ease. A background in programming doesn’t go astray either.
Section 3: Colour Cycling
To start with, one of the first things people want to do is to have different colours on the screen. The simplest way to do this is add some simple formulas to the per_frame section. Now, let’s say you wanted to make the colour of the waveform vary through time.
The colour is defined by three values, one for each of the main colour components (red, green, and blue), each in the range 0 to 1 (0 is dark, 1 is full intensity). You could use something like this:
wave_r = wave_r + 0.5*sin(time*1.13);
wave_g = wave_g + 0.5*sin(time*1.23);
wave_b = wave_b + 0.5*sin(time*1.33);Note: The colour cycling code needs to be in the per_frame section.
It’s nice to stagger the frequencies (1.13, 1.23, and 1.33) of the sine functions for the red, green, and blue colour components so that they cycle at different rates, to avoid them always being the same (which would create a greyscale wave).
Remember that the sine (and cosine) waves have a range of -1 to 1 so if you use these waves with the range 0 to 1 you will have to modify them.
0.5 + 0.5*sin(time) = 0.5 + (-0.5 to 0.5) = 0 to 1This will generate the range 0 to 1 over a period of 6.28 seconds (2π) for a complete cycle. If you want to speed up the cycle, multiply the time variable. E.g. 0.5*sin(2*time) — the period is now 3.14 seconds. And to slow it down, divide.
If you want the colour variable focused on a stricter range, alter the equation. For example, to generate a more “redder” image you may want the range 0.5 to 1 for wave_r:
0.75 + 0.25*sin(time) = 0.75 + (-0.25 to 0.25) = 0.5 to 1Also you can base the colour output on the sound variables (bass, treb_att, etc) to create colours based upon what the music is doing.
Section 4: Variables — A Basic Tutorial
There are hundreds of different effects that can be achieved with MilkDrop. The initial advice is simple: create a blank preset with no zoom, no rotation, no warp, no colours, etc. Here is one created for you:
[preset00]
fRating=3.000000
fGammaAdj=1.000000
fDecay=0.925000
fVideoEchoZoom=1.006596
fVideoEchoAlpha=0.000000
nVideoEchoOrientation=3
nWaveMode=7
bAdditiveWaves=1
bWaveDots=0
bModWaveAlphaByVolume=1
bMaximizeWaveColor=0
bTexWrap=0
bDarkenCenter=0
bMotionVectorsOn=0
bRedBlueStereo=0
nMotionVectorsX=12
nMotionVectorsY=9
bBrighten=0
bDarken=0
bSolarize=0
bInvert=0
fWaveAlpha=4.099998
fWaveScale=1.285751
fWaveSmoothing=0.630000
fWaveParam=0.000000
fModWaveAlphaStart=0.710000
fModWaveAlphaEnd=1.300000
fWarpAnimSpeed=1.000000
fWarpScale=1.331000
fZoomExponent=1.000000
fShader=0.000000
zoom=0.999514
rot=0.000000
cx=0.500000
cy=0.500000
dx=0.000000
dy=0.000000
warp=0.010000
sx=1.000000
sy=1.000000
wave_r=0.650000
wave_g=0.650000
wave_b=0.650000
wave_x=0.500000
wave_y=0.500000
ob_size=0.500000
ob_r=0.010000
ob_g=0.000000
ob_b=0.000000
ob_a=0.000000
ib_size=0.260000
ib_r=0.250000
ib_g=0.250000
ib_b=0.250000
ib_a=0.000000Now run MilkDrop and press F8 to change to this directory. Press M for the Menu and use keys to navigate.
Using the zoom variable as an example, increase and decrease the values to see what the effects are like. Small changes in the zoom equate to quite noticeable effects. The zoom’s default normal value is 1 and small amounts of zoom are say 0.9 to 1.1. If you make the zoom 20 it makes little difference compared to 5 as it is out of a sensible range.
Now it is time to start to edit the code. There are 2 sections: per_frame and per_pixel.
Focus on the per_pixel equation and add the following line:
zoom = 1 + 0.1*sin(ang)Remember sine waves have a range of -1 to 1, therefore the range of this equation is 1 + (0.1 to -0.1) which equates to 0.9 to 1.1.
Many authors tend to use multiplication by the reciprocal rather than division:
zoom = 1 + 0.1*sin(ang) // preferred
zoom = 1 + sin(ang)/10 // avoidDivision on a PC is very slow (27 clock cycles vs. 3 for addition/subtraction/multiplication).
Now edit the line to create different effects:
zoom = 1 + 0.1*sin(-ang)
zoom = 1 + 0.1*sin(1/ang)
zoom = 1 + 0.1*sin(2*ang)
zoom = 1 + 0.1*sin(3*ang)
zoom = 1 + 0.1*sin(-2*ang)
zoom = 1 + 0.1*sin(ang/2)Then try the same equation with rad, x and y instead of ang. And mix these with time:
zoom = 1 + 0.1*sin(time + rad)This will create an effect based upon the sin of the radius, which changes over time, like a ripple spreading inward/outward.
Also try cos instead of sin. Maybe try the tan command. The abs command generates only positive values:
zoom = 1.1 + abs(0.1*sin(ang))Also try - instead of + in the equations. Then if you want to go crazy:
zoom = 1.12 + sin(-2*ang)/9 - cos(rad + (2*x-y))/11or
zoom = 1 + sin(sin(time*rad))/10 - cos(x/y)/30Section 5: Additional Per_Pixel Effects
Now let’s move the focus from zoom to other variables. Another main one is rot for rotation.
Follow the same steps as for zoom but change zoom for rot. The rot default value is 0 rather than 1, so adjust the equations by removing the “1 +” at the start. The reasonable range is about -0.1 to 0.1:
rot = 0.1*sin(ang)dx and dy effects are some of the most powerful effects in MilkDrop but the range is very delicate — instead of 0.1 as the multiplier use say 0.01. Dx and Dy are so powerful you can use these values alone to create effects like zoom and rotation.
Warp is tempting to use but strongly discouraged as it is such a powerful effect — it takes away a lot from intentional effects. Try to avoid.
Now combine a few of these effects together with basic colour cycling and you should be able to create a simple Geiss-like effect.
Section 6: Brightness Control
The brightness of the waveform is controlled in part by opacity. This is available from the menus and cannot be changed in per_pixel or per_frame sections.
There are 2 states: opacity determined by volume, or not by volume. If not by volume, the waveform will always be visible. If by volume, no music means no waveform.
If the image appears too dark, look at decay/sustain. In the menu it’s called sustain; in equations it’s called decay. They’re the same variable. Decay controls the eventual fade to black. Realistically the range is 0.9 to 1. Beware: values at or just below 1 (say 0.998) may eventually turn the screen completely white.
Note: Decay cannot be used in a per_pixel capacity — it is only for per_frame.
High decay tends to create “grey” effects. To counter this, adjust gamma. Gamma controls the brightness of colours and cannot be changed in per_frame or per_pixel — only via the menu or .milk file. Changing gamma from 1 to 2 can reduce speed by ~50%.
Section 7: Advanced Effects
7.1 Video Echo
Video echo creates a mirror image superimposed on the screen. Three factors control it:
Alpha: Opacity of the 2nd layer. 0 = off, 1 = only mirror image, 0.5 = half-half mix.
Scale (zoom in preset code): Size of the second layer. 1 = identical, 2 = double, 0.5 = half.
Orientation:
0 = Normal
1 = Flip on X axis
2 = Flip on Y axis
3 = Flip on both X and Y axesVideo echo costs performance — you may lose frames per second.
7.2 Borders
Introduced in version 0.99f. Two parts: inner border and outer border, each with size, opacity, and RGB colour controls.
Size (ob_size/ib_size): Thickness of border, range 0 to 0.5.
Opacity (ob_a/ib_a): 0 = transparent, 1 = opaque.
Colour (ob_r, ob_g, ob_b / ib_r, ib_g, ib_b): Same RGB control as the waveform.
7.3 Motion Vectors
Dynamic Motion Vectors introduced in version 1.2.
Placement (mv_x, mv_y): Maximum 64 vectors on X, 48 on Y. Fractional values supported.
Opacity: 0 = transparent, 1 = opaque.
Colour (mv_r, mv_g, mv_b): Same RGB control as the waveform.
Section 8: Per_Pixel Values Explained
Basic diagram showing positions and their X, Y, Ang and Rad values:
1----------2-----------3
| | |
| | |
4----------5-----------6
| | |
| | |
7----------8-----------9
| Point | X | Y | Ang | Rad |
|---|---|---|---|---|
| 1 | 0 | 0 | 3π/4 (2.356) | 1 |
| 2 | 0.5 | 0 | π/2 (1.57) | √2/2 |
| 3 | 1 | 0 | π/4 (0.785) | 1 |
| 4 | 0 | 0.5 | π (3.1415) | √2/2 |
| 5 | 0.5 | 0.5 | 0 | 0 |
| 6 | 1 | 0.5 | 0 | √2/2 |
| 7 | 0 | 1 | 5π/4 or -3π/4 | 1 |
| 8 | 0.5 | 1 | 3π/2 or -π/2 | √2/2 |
| 9 | 1 | 1 | 7π/4 or -π/4 | 1 |
Where π = 3.1415… and √2/2 = 0.707
Sine and cosine waves are used extensively in MilkDrop. Learn what a sine curve is and how changes to the equation affect it. Understand the relationship between sin and cos — they are essentially the same but out of phase.
As you push the limits, you’ll need to understand tangent curves, logarithms, squares, cubes, exponentials, arcsin, arccos and arctan. All of this will appear in a decent maths textbook.
Section 9: Preset Walkthroughs
9.1 Introduction
These are a collection of tutorials which give step-by-step descriptions of various presets, teaching about different variables and how they are used without overwhelming you.
9.2 Tutorial 1 — Approach
Fire up MilkDrop and load Geiss’ preset called “Approach” (hit ‘L’ to bring up the load preset menu). Turn on scroll lock to prevent the preset from changing.
Static Values
Hit ‘M’ for the preset editing Menu. Navigate with arrow keys.
Waveform settings: wave type 2, adjustable size, smoothing, mystery parameter, opacity, position (X/Y at 0.5 = centre), colour (R, G, B from 0 to 1), dots mode, volume-modulated opacity, additive drawing, and colour brightening.
Augmentations: Border effects and motion vectors. Motion vectors show what’s happening to pixels at any point — white dots with tails showing direction and speed.
Motion: Zoom (1 = none, >1 = zoom in, <1 = zoom out), zoom exponent (perspective), warp, rotation, translation (dx/dy), and scaling (sx/sy).
Post Processing: Sustain level, darken centre, gamma adjustment, hue shader, video echo (alpha, scale, orientation), texture wrap, stereo 3D, and colour filters.
Per-Frame Equations
wave_x = wave_x + 0.150*( 0.60*sin(2.121*time) + 0.40*sin(1.621*time));
wave_y = wave_y + 0.150*( 0.60*sin(1.742*time) + 0.40*sin(2.322*time));
wave_r = wave_r + 0.200*( 0.60*sin(0.823*time) + 0.40*sin(0.916*time));
wave_g = wave_g + 0.500*( 0.60*sin(0.900*time) + 0.40*sin(1.023*time));
wave_b = wave_b + 0.500*( 0.60*sin(0.808*time) + 0.40*sin(0.949*time));
rot = rot + 0.002*sin(time+0.073);
decay = decay - 0.03*equal(frame%30,0);Moving the waveform (wave_x, wave_y): The equations add two staggered sine waves together (multiplied by 0.60 and 0.40), creating a wobbling value between -1 and 1, then scaled by 0.150 and added to the menu value of 0.5. Result: wave_x moves between 0.35 and 0.65.
Colour cycling (wave_r, _g, _b): Same structure. Different multipliers for time create different cycling rates, preventing repetitive patterns.
Rotation: rot = rot + 0.002*sin(time+0.073) — very subtle rotation oscillating between -0.002 and 0.002.
Decay: decay = decay - 0.03*equal(frame%30,0) — every 30 frames, briefly reduces decay by 0.03. The equal() function returns 1 when both arguments match, 0 otherwise. The % operator gives the remainder after division.
9.3 Tutorial 2 — Tornado
Load the preset “Tornado.” The screen twisting effect is simple to produce yet impactful.
Per-Frame Equations
wave_r = wave_r + 0.400*( 0.60*sin(0.933*time) + 0.40*sin(1.045*time) );
wave_g = wave_g + 0.400*( 0.60*sin(0.900*time) + 0.40*sin(0.956*time) );
wave_b = wave_b + 0.400*( 0.60*sin(0.910*time) + 0.40*sin(0.920*time) );
zoom = zoom + 0.023*( 0.60*sin(0.339*time) + 0.40*sin(0.276*time) );
rot = rot + 0.030*( 0.60*sin(0.381*time) + 0.40*sin(0.579*time) );
decay = decay - 0.01*equal(frame%6,0);Static zoom is 1.031 (always zooming in) with exponent 2.1 for perspective. Warp amount is a subtle 0.309. The zoom equation varies between 1.008 and 1.054.
Per-Pixel Equations
rot = rot + (rad-0.4)*1.7*max(0,min((bass_att-1.1)*1.5,5));Breaking it down:
rot = rot +...— modifying rotation per-pixel (using per-frame result)(rad-0.4)*...— multiplier varies by distance from centre. Positive when rad > 0.4, negative otherwisebass_att-1.1— music response; bass_att is the percentage change in bass between framesmin((bass_att-1.1)*1.5, 5)— caps the value at 5max(0, ...)— ensures value never goes negative- Result: on bass hits, inner pixels rotate one way, outer pixels the other way
9.4 Tutorial 3 — Cruizin’
Covers user-defined variables and the dx/dy translation effects.
Per-Frame Equations
rot = rot + 0.004*( 0.60*sin(0.381*time) + 0.40*sin(0.579*time) );
cx = cx + 0.110*( 0.60*sin(0.374*time) + 0.40*sin(0.294*time) );
cy = cy + 0.110*( 0.60*sin(0.393*time) + 0.40*sin(0.223*time) );The cx and cy variables control the centre of rotation. Despite no static zoom, the zooming motion is achieved through per-pixel code.
Per-Pixel Equations
du = (x-cx)*2;
dv = (y-cy)*2;
q = 0.01*pow(du*du + dv*dv, 1.5);
dx = q*du;
dy = q*dv;User-Defined Variables: du, dv, and q are custom variables — just assign them. Any name works as long as it starts with a letter and isn’t a reserved name (avoid q1 through q5).
The pow() function raises a number to a power. This creates movement that is slow near the rotation centre and fast at the edges — similar to zoom exponent but with more control.
9.5 Tutorial 4 — Shift
Introduces beat detection methodology.
Per-Frame Equations
dx = dx + dx_residual;
dy = dy + dy_residual;
bass_thresh = above(bass_att,bass_thresh)*2
+ (1-above(bass_att,bass_thresh))*((bass_thresh-1.3)*0.96+1.3);
dx_residual = equal(bass_thresh,2)*0.016*sin(time*7)
+ (1-equal(bass_thresh,2))*dx_residual;
dy_residual = equal(bass_thresh,2)*0.012*sin(time*9)
+ (1-equal(bass_thresh,2))*dy_residual;Key concept: User-defined variables persist across frames (unlike built-in variables which reset each frame).
Beat detection pattern: The bass_thresh equation is effectively:
IF bass_att > bass_thresh
THEN bass_thresh = 2
ELSE bass_thresh decays toward 1.3 (96% of difference each frame)This is the same method MilkDrop uses for “Hard Cuts.” When bass_thresh equals 2, a beat was detected. The residual values then get a random displacement; otherwise they keep their previous value.
This beat detection pattern is extremely versatile and can be ported to other presets for intelligent colour cycling, modulated beat detection, and more.
As a small token of gratitude to the Britons for their sympathy and moral support during the events of September 11th, 2001, this document (v1.6) was converted into HTML from MSWord format by Matt W. on March 10th, 2002.
