Vertex Shader Domain Warping with Automatic Differentiation
Dave Pagurek van Mossel
Butter Creatives
May 12, 2024

Figure 1: An airplane model undergoing an animated twist warp. Its vertex positions and normals are
updated in the vertex shader, with the fragment shader visualizing the absolute normal direction as a color.
After applying the warp, the output normals are accurate.

Abstract

exploration and iteration (projects made in Processing [11] are called “sketches” for this reason [5]) andDomain warping is a technique commonly used in cre- thus many commonly employed techniques are ones
ative coding to distort graphics and add visual interest with easily tweakable parameters that can generate a
to a work. The approach has the potential to be used in wide variety of visual results.
3D art as mesh vertices can be efficiently warped using avertex shader in a WebGL pipeline. However, 3D models
packaged for the web typically come with baked-in normal vectors, and these need to be updated when vertex
positions change for lighting calculations to work. This is
typically done via finite differences, which requires parameter tuning to achieve optimal visual fidelity. We present
a method for 3D domain warping that works with automatic differentiation, allowing exact normals to be usedwithout any tuning while still benefiting from hardware
acceleration.

1

One such technique is domain warping, used often
in 2D and some 3D contexts [13]. Given a function
f : Rn 7→ Rn that defines an offset for any point inspace, using the warp function w(⃗x) = ⃗x + f (⃗x), one
can warp any function whose domain is Rn by giving
it w(⃗x) as input instead of ⃗x. It provides a frameworkthat invites exploration, as any offset function canbe used, producing varied, interesting results fromcombinations of simple mathematical building blocks.
While some carefully tuned offset functions may havephysical motivations, such as the wave equations ofDynamic Kelvinlets [3], this is not a requirement. Arbitrary functions are useful tools in the creation ofsurreal or abstract art, a common style in creativecoding [4, 9, 15].

Introduction

Creative coding is an art form that uses the computerand source code as an expressive medium. The proThis technique has the potential to fit nicely into acess of writing creative code is often characterized by 3D workflow on the web: to warp the domain of a 3D1

mesh, one can implement an offset function in a vertex shader, which is the step in the pipeline responsible for transforming vertex positions into screen space
before rasterization. Running on the GPU, this is
likely the most efficient method of domain warping a
triangle mesh. Unfortunately, meshes packaged for a
WebGL pipeline typically bake in a normal vector foreach vertex. If one applies domain warping to vertex
positions without updating the baked normals, they
will no longer be perpendicular to the surface of themesh (visualized in Figure 2b) and will cause later
lighting calculations in the fragment shader to be inaccurate. Using finite differences to approximate updated normals requires parameter tuning for each use
to avoid visual artifacts, making it difficult to use ina general system accepting arbitrary meshes and offset functions. We present an algorithm that leverages
automatic differentiation to generate an updated normal in any circumstance without parameter tuning,enabling 3D domain warping to be used more easily
in web-based creative code environments.

bump mapping and involves parameterizing the surface of a model into two axes, u and v, and providing
a bump height h : R2 7→ R defined for each parameter value [2]. The bump map suggests a new
surface, where each point is moved along its normal
by the height defined by the bump function, yielding p⃗′ = p⃗ + h([up vp ]T )n̂. One can then compute
⃗′

⃗′

p
p
× ∂∂v
. This formulaa perturbed normal n̂′ = ∂∂ution does not depend on a specific parameterizationof the surface, so bump maps can be applied in a
fragment shader by using a local screen-space parameterization [10], relying on derivatives with respect toneighboring pixels (dFdx(position) and dFdy(position)in GLSL.)
Bump mapping intentionally only affects surface
lighting and not the geometry, and given this small
scale, only handles 1D height maps. If one wants
to apply similar techniques at larger scales using 3D
offsets, updated formulae are required. At larger
scales, the vertices must be updated in the vertex
shader, where one does not have screen-space derivatives available. If one were to recalculate normals
using screen-space derivatives in the fragment shader
2 Backgroundbased on the updated vertex positions, the linear inDomain warping. Domain warping in 3D is a terpolation between vertices becomes apparent, makcommon technique used when raymarching signed ing normals appear faceted instead of smooth, shown
distance functions (SDFs) [13], found often in the in Figure 2c.
demoscene and on platforms like Shadertoy [8]. Unlike triangle meshes, SDFs do not have baked nor- Finite differences. SDF raymarching code typmals that need updating: models are represented by ically calculates normals via finite differences, apa function f : R3 7→ R describing, in theory, the proximating the normal (∇f (⃗p) when p⃗ is on the
distance to the surface of the shape at a given point surface) with, using the X axis as an example,in space, and in practice, a conservative estimate of 1 f (⃗p + [h 0 0]T ) − f (⃗p) for a small value h. Whilehsaid distance. Shapes are then rendered by marching this method of calculating normals can also work in(or “sphere tracing”) light rays through the scene, de- a mesh-based WebGL pipeline, it requires simple but
tecting intersections using the scene SDF [7]. While tedious tuning of h to work well. To paint a picdomain warping an inexact SDF can be as simple ture of what picking h involves, its value needs to
as function composition, one cannot make use of ex- be small enough to not lose detail, but not so small
isting triangle-based 3D models without a nontrivial that numerical precision issues become visible. Since
conversion that comes with performance tradeoffs [1]. SDF raymarching is done per pixel, to be as large as

possible without losing visual detail, a recommended
heuristic for picking h is to try to make its footprint inscreen space approximately one pixel: small enoughthat more accurate normals would not be visible, andlarge enough to prevent aliasing. [14]Perturbing normals. In a mesh-based WebGL
pipeline, perturbations that update normals are usedat small scales to add extra texture to the surface
of a model. Traditionally, this technique is called2

(a) Input(b) Warp without adjusting normals(c) Warp with normals from screen-space
derivatives(d) Warp affecting vertices and normals

Figure 2: When applying a sine wave warp to the vertices of a sphere (a), the baked normals will beincorrect (b) unless they are updated to account for the warp. Relying on screen-space derivatives of position
yields faceted normals (c), while computing normals in the vertex shader allows for smooth normals (d).
In the demoscene community, which produced properties and not the whole mesh.
many contemporary SDF techniques, file size conOur method achieves these goals in the following
straints are common (including the entire genre of steps:
64K intros), so fine-tuning of parameters is a common and acceptable tradeoff for smaller code. Our
1. We express derivatives of the warp function given
method, aimed instead at an audience interested
a local tangent and bitangent, which we thenin easy experimentation and expression, sidesteps
use to generate a warped normal. Importantly,
this with a setup that leverages exact derivatives.
the structure of the derivative formula depends
Comparatively, normals achieved through automatic
only on the warp function and accepts any local
differentiation will be more accurate while having
parameterization, enabling one warp shader toequivalent computational cost [6], motivating our apwork on all meshes.
proach and its use of automatic differentiation instead of approximation.
2. We provide a formula for a surface tangent andbitangent given only the surface normal, allowing inputs to Step 1 to be generated in a vertex
3 Methodshader with limited knowledge of the mesh.
The artist provides an offset function f : R3 7→ R3
defining, for an input point in 3D space, a 3D offset
to add to its location. We assume f is continuous anddifferentiable. Each vertex position p⃗ on the mesh M
will be mapped to an updated position p⃗′ = w(⃗p) =p⃗ + f (⃗p).
Each p⃗ from the input mesh comes with a corresponding baked normal n̂. We are solving for thevector n̂′ , representing the surface normal of the deformed mesh M ′ at point p⃗′ . This must be done in a
way that exact derivatives that can be feasibly calculated in a shader, with only knowledge of per-vertex

3. We describe a system to use automatic differentiation to statically generate a shader with thederivatives required for Step 1.

3.1

Updated Normals

Assume one has two unique vectors û and v̂ tangent
to the surface of the input mesh M at p such thatû × v̂ = n̂. One can approximate n̂′ by finding theresult of w on points near p⃗, shifted slightly along alinear approximation of the surface at p⃗ by a small
3value h:a tangent û and bitangent v̂ by finding any vector ŵ
not equal to ±n̂, taking the cross product between itn̂′ ≈and n̂ to get one vector parallel to S, and then taking
the cross product between that and n̂ to get another,w(⃗
p + hû) − w(⃗p) w(⃗
p + hv̂) − w(⃗p)
normalize
×
different vector parallel to S:hh([0 1 0]T , n̂ = [±1 0 0]T
Taking the limit as h → 0, this is equivalent toŵ =[1 0 0]T otherwise
taking directional derivatives of w(⃗p) in the directionsû and v̂:v̂ = normalize(v̂ × n̂)
(2)


∂w(⃗
p
)
∂w(⃗
p
)
û = v̂ × n̂(3)×n̂′ = normalize
∂ û
∂v̂


Since |⃗a × ⃗b| = |⃗a||⃗b| sin θ, to get a unit tangent∂(⃗
p + f (⃗p)) ∂(⃗
p + f (⃗
p))
vector from a cross product, one must normalize the= normalize
×
∂ û
∂v̂
result if the two inputs are not orthogonal. Since ŵ
Since, by definition, û and v̂ are tangent to the and v̂ are guaranteed by construction to not be parsurface at p⃗, the directional derivative of p⃗ in the allel but may not be orthogonal, we must normalize
direction of û or v̂ will be û or v̂ itself, respectively: the result of their cross product in Equation 2 to get
v̂.

 

By combining Equations 2 and 3 with Equation 1,
∂f (⃗
p)
∂f (⃗
p)
′
n̂ = normalizeû +× v̂ +
we can fully compute a new n̂ that can be used for∂ û∂v̂lighting.
Since f is defined in terms of Cartesian coordinates
and not the coordinate space defined by û and v̂, we
3.3 Automatic Differentiation
reframe the above expression
h to⃗′ be in⃗′ terms⃗′ ofi the(p ) ∂f (p ) ∂f (p )The derivatives in Section 3.1 are being used to comJacobian of the offset, J = ∂f∂x:
∂y∂z
pute the Jacobian of the offset function ∈ R3×3 based
3n̂′ = normalize ((û + J û) × (v̂ + J v̂))(1) on an input position ∈ R . Given that the outputs
are greater in number than the inputs, we opt for
To define J, we calculate all the partial first-order forward-mode automatic differentiation.
The derivatives will be used in a shader, which has
derivatives of f , which will be implemented using automatic differentiation, described later in Section 3.3. limited language capabilities and resources. Rather
Importantly, this is independent of the choice of tan- than creating structs in GLSL to create dual numgent vectors: whichever ones we pick, the formula for bers and defining GLSL functions to operate on them,
J does not change, so we do not need to run auto- we instead differentiate at compile time. We express
matic differentiation at runtime. It is sufficient to the offset function in a host language outside of therun it once at compile time, and our runtime choice shader, and from the host language, generate GLSL
of tangent vectors only adds a matrix multiplication source code for the shader that computes both theoffset and its derivatives. For each operation in thewith J.
computation graph, we can generate one line of GLSL
to compute and store the output of the computa3.2 Picking Tangent Vectors
tion, plus an additional line storing the derivative
Without knowledge of the rest of the mesh vertices of that graph node with respect to each indepenand their connectivity, we can make assumptions dent variable. In practice, we output more condensed
about the local surface around p⃗. The normal n̂ im- code, as we are able to compact multiple computaplies that there is a surface plane S passing through p⃗ tion graph nodes up to a specified depth before outand normal to n̂. We can parameterize this plane into putting GLSL, as we find this easier to inspect and4

debug. Listing 1 shows an example of this, with andwithout condensed output.

4

Implementation

Listing 1: Generated GLSL code for the Jacobian of
the offset function f ([x y z]T ) = [0.5 sin(0.005t +T
Targeting the creative coding community, our imple- 2y) 0 0] , with and without condensing the number
mentation comes in the form of the library p5.warp: of intermediate variables in the output. The autoa plugin for the web graphics library p5.js [12] writ- matic output has been manually formatted and inten in JavaScript and GLSL. Artists specify an off- termediate variables have been manually renamed for
set function f in Javascript through our API, a readability.
shader generator using the builder design pattern. // One node per v a r i a b l e :The library provides builder methods corresponding float v1 = millis * 0.005;
to mathematical operators and functions, allowing a float v2 = position . y * 2.0;
float d_v2_by_d_y = 2.0 * 1.0;
computation graph to be built with concise syntax. float v3 = v1 + v2 ;The builder implements static forward-mode auto- float d_v3_by_d_y = 0.0 + d_v2_by_d_y ;matic differentiation by outputting GLSL code con- float v4 = sin ( v3 ) ;d_v4_by_d_y =taining expressions that compute both f and its Ja- floatcos ( v3 ) * d_v3_by_d_y ;cobian J.
float v5 = v4 * 0.5;
The artist does not need to write any GLSL and float d_v5_by_d_y = 0.5 * d_v4_by_d_y ;only needs to interact with our builder API. Artists vec3 offset = vec3 ( v5 , 0.0 , 0.0) ;vec3 d _o ff se t _b y_ d_ y =are free to use loops, make temporary variables, or
vec3 ( d_v5_by_d_y , 0.0 , 0.0) ;create and call functions while interacting with the mat3 jacobian = mat3 (vec3 (0.0) ,builder. These JavaScript constructs act as macrosd_offset_by_d_y ,with respect to the generated shader code, evaluatedvec3 (0.0)
at the shader’s compile time to generate static GLSL. ) ;The API also provides access to inputs such as thecurrent time in milliseconds, the mouse position, or // C o n d e n s e d :offset = vec3 (the canvas size, which get turned into shader uni- vec3
sin ( time * 0.005 + position . y * 2.0)
forms. Artists may define a warp in either model or* 0.5 ,
0.0 ,world space. Listing 2 shows an example of a twist
0.0
warp that animates back and forth over time, us);ing the millis uniform to access time and using a vec3 d _o ff se t _b y_ d_ y = vec3 (JavaScript function as a macro.
0.5 * cos (time * 0.005 + position . y * 2.0
The output of the builder is spliced into a vertex) ) * (0.0 + (2.0 * 1.0) ) ,shader, where the normal-updating math from Sec0.0 ,tion 3 is implemented. The main subroutine of the0.0
shader for a warp function defined in model space is ) ;mat3 jacobian = mat3 (shown in Listing 3.

5

vec3 (0.0) ,d_offset_by_d_y ,vec3 (0.0)

Results);

Visuals. Figure 1 shows frames of an animatedwarp applied to an airplane model, giving it a twisting motion with cartoon squash and stretch. Exam5

Listing 3: A vertex shader snippet calculating an updated normal.
void main () {// Start from a t t r i b u t e s
vec3 position = aPosition ;vec3 normal = aNormal ;Listing 2: An example of defining a twist about thehorizontal axis in p5.warp.
const twist = createWarp (function ({glsl ,
millis ,position}) {function rotateX ( pos , angle ) {const sa = glsl . sin ( angle ) ;const ca = glsl . cos ( angle ) ;return glsl . vec3 (pos . x () ,pos . y (). mult ( ca ). sub ( pos . z () . mult ( sa ) ) ,pos . y (). mult ( sa ). add ( pos . z () . mult ( ca ) ));};// Splice in auto - g e n e r a t e d code .
// This defines ‘ vec3 offset ‘ , and// three ‘ vec3 ‘ s for each column of// the J a c o b i a n : ‘ dodx ‘ , ‘ dody ‘ ,// and ‘ dodz ‘$ { o u t p u t O f f s e t A n d D e r i v a t i v e s () }position += offset ;vec3 w =( normal . y == 0. && normal . z == 0.)? vec3 (0. , 1. , 0.): vec3 (1. , 0. , 0.) ;vec3 v =normalize ( cross (w , normal ) ) ;vec3 u = cross (v , normal ) ;mat3 jacobian =mat3 ( dodx , dody , dodz ) ;normal = normalize ( cross (u + jacobian * u ,v + jacobian * v));

const rotated = rotateX (position ,position . x (). mult (0.02). mult ( millis . div (1000) . sin () ));return rotated . sub ( position ) ;// Apply camera t r a n s f o r m s// and output
gl_Position =uP * uVM * vec4 ( position , 1.) ;});// Pass on to f r a g m e n t shadervNormal = uN * normal ;}6ples of other warps applied to models can be seenin figures 2d, 3, 4 and 5. For easier inspection of
accuracy, they have been visualized with a fragment
shader that outputs the absolute value of the updated
normal as the color. Figure 7 shows warps applied tomodels as part of larger compositions, with lighting
calculations done in their fragment shaders.

(a) Input(b) Warped

Figure 5: Frames of an animated warp inspired by the“genie” animation while minimizing a window introFigure 3: A cube (a) passed through a warp made duced in Mac OS X.
with sine waves in each axis (b).
ber of nodes in the computation graph. The results
are listed in Table 1. When increasing the vertex
count of the model, the MacBooks maintained 60fps,
and the tab on the phone crashed before losing 60fps,
suggesting that hardware memory limitations may be
the primary bottleneck for model size rather thanthe performance of p5.warp with standard warp complexity. We also experimented with increasing thewarp complexity by stacking random sine waves until reaching a target number of nodes in the computation graph. This had a more noticeable effect onframe rates: the phone begins to struggle at 1,500
nodes, and the 2015 MacBook begins to show signsof slowing down at 3,000. We consider these to be acceptable limits, as our manually coded warps have asmaller computation graph by a factor of 10, leaving
sufficient performance buffer room.

Performance. As a test of a typical use case, wemeasured the performance of the animation shown in
Figure 1. It runs p5.warp on a mesh with around
14,000 vertices using a warp with 150 nodes in
its computation graph, rendering onto a 1200×1200
pixel canvas. It runs at 60 frames per second, theimposed browser limit on a 60Hz display, on a 2021
MacBook Pro with an M1 Pro chip, a 2015 Intel MacBook Pro with integrated GPU, and a 2021 Motorola
Edge Android phone in Google Chrome.
We stress-tested our method by increasing boththe number of vertices in the model and the num-

5.1(a) Input [16](b) Warped with (c) Warped bya twistflattening

Discussion

Model resolution. Given two points p⃗ and ⃗q connected by a straight edge on a mesh M , the ideal
Figure 4: A bunny (a) retaining correct normals form of the domain warped mesh has p⃗′ = w(⃗p) andwhen passed through warps that twist about the X q⃗′ = w(⃗q) connected by a curve C(t) = w(t⃗p + (1 −axis (b) and even after being flattened into the YZ t)⃗q), t ∈ [0, 1]. Since the outputted mesh in WebGL
plane (c).
will still connect p⃗′ and q⃗′ with the straight line seg7

14,000 vertices, 150 nodes
500,000 vertices, 150 nodes
14,000 vertices, 1,500 nodes
14,000 vertices, 3,000 nodes

2021 M1 Pro MacBook
Pro
60fps
60fps
60fps
60fps

2015 Integrated GPU Intel MacBook Pro
60fps
60fps
60fps
50fps

2021 Motorola Edge
60fps
Crash
26fps
10fps

Table 1: Frame rates achieved using our method on a variety of hardware, model sizes (number of vertices),and warp complexities (number of nodes in the computation graph.)ment L(t) = tw(⃗p) + (1 − t)w(⃗q), the visual qualityof a deformed mesh depends highly on the distance
between the ideal curve C(t) and its approximation
L(t). The effect of model resolution on a high frequency warp is shown in Figure 6.

produce zero-magnitude vectors as normals. Equation 1 breaks down if either operand of the cross product is a zero vector, or if both operands are equal toeach other. The former case happens if, for some tangent vector ŵ, J ŵ = −ŵ, or in other terms, if J has
an eigenvalue of -1. Rearranging the equation for thelatter case, we find it is a special case of the formercase:û + J û = v̂ + J v̂J(û − v̂) = −(û − v̂)J ŵ = −ŵ,(a) Input(b) Warped, 20 (c) Warped, oneedges/sideedge/sideŵ = û − v̂

A warp where ∃ ŵ | J ŵ = −ŵ is one that maps
some axis to a constant. As an example, take f (⃗p) =−⃗p + ⃗k, which offsets any input to a constant in allaxes, turning it into the point ⃗k. Its Jacobian matrix
is −I3 , which has an eigenvalue of -1.
The previous example choice of f causes all normals to be degenarate, but by mapping all vertices toa single point, the mesh will have no surface area andno fragments will render, eliminating the problem. If
all vertices are mapped to a line, which also has nosurface area, similarly, no fragments will be rendered.
If all vertices are mapped to a plane, as would happen with f (⃗p) = [−p1 , 0, 0]T , which flattens to the
YZ plane, then some fragments will be rendered. Input normals tangent to the plane will have tangent
vectors that get mapped to zero, being perpendicular to the plane. A face whose vertices are all suchnormals, if the normals are not askew from the facenormal, is itself perpendicular to the YZ plane, so it
will not get rendered, as it warps into a new face withzero area. Given a face where just some of its vertices have such normals, fragments in the middle of

Figure 6: A cube (a) passed through a twist warp
when the cube has been subdivided into 20 edges per
side (b) and one edge per side (c). Without sufficient polygon detail, the linear interpolation between
vertices fails to capture the full warp, producing selfintersections and less accurate normals interpolated
over faces.

Continuum approximation. Warp functions aredefined over R3 as a continuum without knowledgeof the true shape of the model. While this method
has been used to implement efficient hand-animatedimitations of physical phenomena such as soft bodyspringiness or cloth in the wind, this method is notsuited for more accurate physics simulations whereforces propagate through the true shape of the mesh.
Degenerate offsets. Rendering is rarely affectedby the cases where the equations in Section 3 would
8

the face will have normals interpolated between somedegenerate and some valid normals. In practice, thisis not a problem: degenerate normals are zero vectors, and in the barycentric interpolation performedby WebGL, one or two components being zero simply reduces the magnitude of the weighted sum of theremaining vector components. Since the normal vector is normalized back to unit length in the fragment
shader after interpolation, resulting normals will still
be valid. Figure 4c shows the result of flattening to
the YZ plane, where all resulting normals point in
the X axis.
To summarize, offset functions that produce degenerate normals do not render fragments unlessthey collapse inputs to a plane. As long as faces
whose vertices all have parallel normals form a surface that is itself perpenducular to those normals,
plane-collapsing functions still produce correct results due to barycentric interpolation and normalization of normals.

6functions. Artists in the creative coding community
are open to exploring math directly, but future work
could include studies of more intuitive forms of warp
function control.

References[1] J.A. Baerentzen and H. Aanaes. Signed distance computation using the angle weighted
pseudonormal. IEEE Transactions on Visualization and Computer Graphics, 11(3):243–253,
2005. doi:10.1109/TVCG.2005.49.
[2] James F. Blinn. Simulation of wrinkled surfaces.
SIGGRAPH Comput. Graph., 12(3):286–292,
aug 1978. doi:10.1145/965139.507101.
[3] Fernando De Goes and Doug L. James. Dynamic kelvinlets: Secondary motions based on
fundamental solutions of elastodynamics. ACM
Trans. Graph., 37(4), jul 2018. doi:10.1145/
3197517.3201280.

Conclusion and Future Work

[4] Matt DesLauriers. This is created with dense
vertical polylines warped by a noise function. Here is a close-up of the final renders, as well as three generations using lower
line counts to visualize the algorithm., October 2018. URL: https://twitter.com/mattdesl/status/1047094498562625536.

Domain warping, a common and versatile techniquein creative coding, is difficult to use in 3D due tothe need to recompute surface normals. We describe
a method of computing updated normals that canbe implemented in a vertex shader and present a library implementing such a shader, providing automatic generation of the derivatives is requires. One
can then write any warp without worrying about incorrect lighting or parameter tuning, enabling artists
to freely iterate.
With support for the WebGPU API now enabled
by default in Google Chrome and the access to modern hardware capabilities it provides, there may bepotential in the future to create new render pipelines
using compute shaders that address current WebGL
limitations. Our method currently lacks hardwareaccelerated adaptive subdivision based on the warp
function, which could perhaps be enabled with more
GPU flexibility.
Finally, the ability to warp using arbitrary functions raises the human-computer interaction questionof how to best to let artists sculpt abstract warp

[5] Benjamin Jotham Fry. Computational Information Design. PhD thesis, Massachusetts Institute of Technology, 4 2004. URL: https://benfry.
com/phd/dissertation-110323c.pdf.
[6] Andreas Griewank et al. On automatic differentiation. Mathematical Programming: recent developments and applications, 6(6):83–107, 1989.
[7] J. C. Hart, D. J. Sandin, and L. H. Kauffman. Ray tracing deterministic 3-d fractals. In
Proceedings of the 16th Annual Conference on
Computer Graphics and Interactive Techniques,
SIGGRAPH ’89, page 289–296, New York, NY,
USA, 1989. Association for Computing Machinery. doi:10.1145/74333.74363.
9

(a) Repeated, warped copies of a 3D scan of a person, creating asurreal composition.

(c) A recursive hand structure using
world-space warping to make fingers
wiggle.

(b) A warped spoon, playing on
The Matrix and René Magritte’s The
Treachery of Images.

(d) A bulge effect added to 3D text based on the mouse location.

(e) Pretend cloth physics applied via a warp to a rotating carousel of shirts, where each is a textured plane.

Figure 7: Examples of domain warping using p5.warp in different sketches, using lighting calculations intheir fragment shaders that make use of updated normals.
10[8] Pol Jeremias and Inigo Quilez. Shadertoy: A snapshot of both repositories is present in the supLearn to create everything in a fragment shader. plemental material in a .zip file.
In SIGGRAPH Asia 2014 Courses, SA ’14,
New York, NY, USA, 2014. Association for
Computing Machinery. doi:10.1145/2659467. Author Contact Information
2659474.
Dave Pagurek van Mossel[9] Ben Kovach. Art Blocks gave me the opportu- Butter Creatives
nity to stretch this system to its limits. Edifice dave@davepagurek.com
plays extensively with color, layout, texture, perspective, and domain warping to produce images
that still consistently surprise me after generating tens of thousands of outputs. Here’s Ropsten
41, November 2021. URL: https://twitter.com/
bendotk/status/1455232555561754626.
[10] Morten S. Mikkelsen.
Bump mapping unparametrized surfaces on the gpu. Journal ofGraphics, GPU, and Game Tools, 15:49 – 61,
2010. URL: https://mmikk.github.io/papers3d/
mm sfgrad bump.pdf.
[11] Processing Foundation. Processing, 2001. URL:
https://processing.org.
[12] Processing Foundation. p5.js, 2014. URL: https://p5js.org.
[13] Inigo Quilez. Domain Warping, 2002. URL:
https://iquilezles.org/articles/warp/.
[14] Inigo Quilez. Normals for an SDF, 2015. URL:
https://iquilezles.org/articles/normalsSDF/.
[15] Gaëtan Renaudeau. Cirque de lumières, 2021.
URL: https://gen.art/drops/lumieres.
[16] Stanford University. The Stanford 3D Scanning Repository, 1994. URL: https://graphics.
stanford.edu/data/3Dscanrep/.

Index of Supplemental Materials
Source code for p5.warp and its GLSL-building capabilities can respectively be found at:• https://github.com/davepagurek/p5.warp• https://github.com/davepagurek/glsl-autodiff
11