# How do I make a curve in the shape of a simple wave form (sine)?

I am trying to create a curve with a simple wave form to use it as animation path. Although I have a bunch of possibilities to add different curves:

.. I did not find a solution yet. This is exactly what I want to achieve:

I did this by adding a Nurbs Curve and in Edit Mode deformed it, duplicated/rotated/replaced it so that I have a longer curve (because I cannot use Array Modifier for curves).

Problem is: When my object follows the path (Follow Path Contraint) it will stop directly after the first original section of the curve.

So: Is there an Addon, a setting for the default available curves by which I can achieve to get a sine wave path? Google only showed me older Q&A with Python scripting but I think there must (hopefully) be another way?

• I think the best solution will be python script. I'm sure if it's acceptable then someone (me if nobody beats me to it) could produce a simple script to generate a sine wave to a specified number of cycles. Feb 1, 2018 at 15:02
• It's possible to add math function mesh (via Add Mesh: Extra Objects, Z Math Surface)) and then convert it to curve. I didn't hear about the same for curves Feb 1, 2018 at 15:26
• Does it have to be a perfect function curve or is an approximation OK? Feb 2, 2018 at 5:41
• @MrZak thx, I already have the "Add Curve: Extra Objects" Addon installed, but with this I did not figure out a proper solution yet (maybe I just don't see it :-)) - I will try the Z Math Surface approach though
– ho.s
Feb 4, 2018 at 8:44

Animation Node can be used here, a simple sine wave spline can be generated as follows:

Animating an object along the wave can be done by evaluating it at some point as follows:

Where the divide controls the speed of the motion.

To align the rotation of the object, you can compute the angle that the tangent to the curve makes with the x axis, the tangent is equal to the derivative which is dy/dt(sin(t)) = cos(t) so the angle is equal to arctan(cos(t)), and the node tree becomes as follows:

Here's my take on this...

Open a Text Editor window and paste the following code :

import bpy
import math

def create_sine(numCycles = 1, stepsPerCycle = 16, curvelen=2, yscale=1):

curve = bpy.data.curves.new('sinepath', type='CURVE')
curve.dimensions = '2D'
curve.resolution_u = 1
spline = curve.splines.new('NURBS')

#cursor = bpy.context.scene.cursor_location
xscale = float(curvelen)/stepsPerCycle/numCycles

for x in range(0, stepsPerCycle * numCycles+1):
y = math.sin(float(x) / stepsPerCycle * math.pi*2)

#Add first point for start of Nurbs (needs extra point)
if x == 0:
spline.points[0].co = (x*xscale,y*yscale, 0.0,1)

spline.points[-1].co = (x*xscale, y*yscale,0.0,1)

spline.points[-1].co = (x*xscale, y*yscale,0.0,1)

curveObject = bpy.data.objects.new('sinepath', curve)

create_sine(numCycles = 4, stepsPerCycle = 16, yscale=1,curvelen=2)


Name it something like 'sine.py'.

Note the last line that invokes the function - amend the parameters as you require.

• numCycles is the number of complete sine waves
• stepsPerCycle is the number of points per cycle
• yscale is the amplitude of the wave
• curveLen is the length of the curve

Note that the curve will be created at the world origin.

Click Run Script to run the script and a new Nurbs path should be created :

Click the path and go into Edit mode (Tab) and back to Object mode to ensure the new geometry is committed (I think I'm just missing a function call for this - I'll update it when I figure it out). You can then use the wave for a Follow Path constraint.

UPDATE : Later versions of Blender (I haven't verified the actual point this changed) use a slightly different API and the amended code is as follows :

import bpy
import math

def create_sine(numCycles = 1, stepsPerCycle = 16, curvelen=2, yscale=1):

curve = bpy.data.curves.new('sinepath', type='CURVE')
curve.dimensions = '2D'
curve.resolution_u = 1
spline = curve.splines.new('NURBS')

#cursor = bpy.context.scene.cursor_location
xscale = float(curvelen)/stepsPerCycle/numCycles

for x in range(0, stepsPerCycle * numCycles+1):
y = math.sin(float(x) / stepsPerCycle * math.pi*2)

#Add first point for start of Nurbs (needs extra point)
if x == 0:
spline.points[0].co = (x*xscale,y*yscale, 0.0,1)

spline.points[-1].co = (x*xscale, y*yscale,0.0,1)

spline.points[-1].co = (x*xscale, y*yscale,0.0,1)

curveObject = bpy.data.objects.new('sinepath', curve)

create_sine(numCycles = 4, stepsPerCycle = 16, yscale=1,curvelen=2)


The changes are that the 'splint.points.add(...)' function now requires an argument to indicate the number of points to add (I simply added '1') and also the linking of the new curve to the list of objects now requires the 'collection'.

Note that I'll leave both versions since the amended code will not run on earlier versions of Blender.

• Thank you very much for your solution - it made me open the text editor and run a script the very first time, thanks again for that. It worked fine: The "sinepath" was created successfully and I could use it for my Follow Path constraint. BUT I do not know why, it deformed my object (cube looks like a trapezoid). So I am going with another solution, but your answer was very helpful, too.
– ho.s
Feb 4, 2018 at 8:26
• The script needs to be updated. Some of the API has changed since then (as of 2023). Oct 25, 2023 at 21:40
• @PauloCarvalho I've updated the answer to include a script amended to allow for the API changes for later versions of Blender. Essentially the 'points.add' needs to specify the number of points (1) and the 'objects.list' needed to include the 'collection' reference. Nov 1, 2023 at 16:41
• @RichSedman Due to deadline constraint, I ended up making my sine curve using the Bezier curve approach. Good to count this as an approach to make a more accurate sine wave. Thanks anyway. Nov 3, 2023 at 16:01

First of all, I think the reason the follow path constraint stops is because it appears you have breaks in your curve after every repeated segment.Hard to say form the images alone though. If that is the case, since there is no merge option for curves, then select both points, fill them with F and scale them to 0. Then select one point, delete it, and use the dissolve vertices option. This will merge the two points, which should fix your issue.

As Mr Zak mentioned, the Extra objects addon has a Z Math surface, and by using the two middle loops and collapsing them into one, we get a perfect sine curve. This can be duplicated and flipped, then using vertex snapping it can be aligned with the first section. Once you have one wave, it can be duplicated as many times as necessary, then the doubles removed with W, and converted to a curve with Alt+C.

I would suggest an alternative though, one that would give you much more control. This may or may not work depending on your exact situation, but if all you want is motion along a sine curve along one axis only than this will work great.

First, animate your object along the axis you want it to travel. In my example I will use a simple mesh arrow so we can easily see where it is pointing, and I will have it move along the X axis. I animated it to move 10 units in 100 frames. Animate your object using the LocRot keyframe, so that we will have access to the location and rotation curves. The rest will all be done using the graph editor.

Here is where I'm at so far:

The first step is to change the interpolation type on the X location curve to be linear, so that it moves with uniform motion, instead of accelerating then decelerating. So we can select the curve, press T and choose linear. This should give us a nice straight line.

Like so:

Now, in order to get our object to move along a sine curve, we need to modify the Z location and the Y rotation. To do this, we will use the modifiers in the Graph Editor. Open the properties Panel in the Graph Editor with N and choose the modifier tab. Now select the Z location from the list on the left, and add a Built-In Function modifier. The default function is Sine, so we could call this done, but the frequency of the wave is pretty high, so we can turn that down with the Phase Multiplier option. I set it to .1 so that it has nice smooth motion.

Here are the setting so far:

The final step is to add a sine function to the Y rotation curve. So lets select that one and add the modifier. Make sure you copy all of the setting from the Z location modifier to the Y rotation one, otherwise they will be out of sync. Since we are dealing with rotation though, the same amplitude will result in a much taller wave, which is what we want. In my case, all I had to change was the Phase Multiplier. Is you play the animation, it won't look right, and that is because we need to offset the Y rotation curve. Since we want the object to have no rotation and the peaks and troughs, and have the maximum rotation while halfway in between, we will need to offset the curve by pi/2, or approximately 1.571 (this is what blender rounds it to). Depending on the rotation of your object as well, it may have to be negative. One you set the Phase Offset to that value, we are finished.

Here's the Y rotation curve and modifier:

And here is the final result:

• Thanks a lot for your solution and detailed explanation! I gave the "Extra Objects - Z Math surface" method a shot. I have a perfectly closed and smooth sine curve now but couldn't get the object to follow it properly. To explain would make a whole new question i suppose :-) But your approach is nice, it helped me a lot to understand more about the possibilities of modifying animation curves (did not do this before).
– ho.s
Feb 4, 2018 at 10:01

It's my solution, it simple, but works for me 😀

1. Create a curve
2. Subdivide it
3. Make sure all of the handles are Vectors
4. Select all points , go to [Select] - [Checker Deselect]
5. Move them up a little bit, or any direction you like to, then select all points again, change handles to Aligned
6. Finishing it

This'll get you most of the way there. I don't know how to target curve handles in Python, so you'll have to right-click the furthest handle nodes then delete >> vertices. You'll do the same with the little initial floating segment below. Easy enough to do. I'm just not that familiar with scripting curves in Blender.

import bpy
import math

strokes = []
for x in range(-10, 10):
y = math.sin(x)
strokes.append({ "name":"",      "location":(x, y, 0),
"mouse":(0, 0), "pressure":1,
"size":0,       "pen_flip":False,
"time":0,       "is_start":False
})