Maybe the surface of the water is like a wire: If you pull on some point of the wire, the points next to that point will be pulled down too. All points are also attracted back to a baseline. I sketched that idea in Lua using [LÖVE][1] and got this: ![rough blue wave, formed of points, with red baseline][2] It's a plausible result. I'm sure I've butchered the physics though: I have no idea what I'm doing beyond initial intuitions and lots of [Hooke's Law][3]! Anyway, here's my `main.lua`. Feel free to read, copy or use as toilet paper. -- Resolution of simulation NUM_POINTS = 200 -- Width of simulation WIDTH = 700 -- Spring constant for forces applied by adjacent points SPRING_CONSTANT = 0.1 -- Sprint constant for force applied to baseline SPRING_CONSTANT_BASELINE = 0.1 -- Vertical draw offset of simulation Y_OFFSET = 300 -- Make points to go on the wave function makeWavePoints(numPoints) local t = {} for n = 1,numPoints do -- This represents a point on the wave local newPoint = { x = n / numPoints * WIDTH, y = Y_OFFSET, spd = {y=0}, -- speed with vertical component zero mass = 1 } t[n] = newPoint end return t end wavePoints = makeWavePoints(NUM_POINTS) -- Update the positions of each wave point function updateWavePoints(points, dt) for n,p in ipairs(points) do -- force to apply to this point local force = 0 -- forces caused by the point immediately to the left or the right local forceFromLeft, forceFromRight if n == 1 then -- wrap to left-to-right local dy = points[# points].y - p.y forceFromLeft = SPRING_CONSTANT * dy else -- normally local dy = points[n-1].y - p.y forceFromLeft = SPRING_CONSTANT * dy end if n == # points then -- wrap to right-to-left local dy = points[1].y - p.y forceFromRight = SPRING_CONSTANT * dy else -- normally local dy = points[n+1].y - p.y forceFromRight = SPRING_CONSTANT * dy end -- Also apply force toward the baseline local dy = Y_OFFSET - p.y forceToBaseline = SPRING_CONSTANT_BASELINE * dy -- Sum up forces force = force + forceFromLeft force = force + forceFromRight force = force + forceToBaseline -- Calculate acceleration local acceleration = force / p.mass -- Apply acceleration (with damping) p.spd.y = 0.89 * p.spd.y + acceleration -- Apply speed p.y = p.y + p.spd.y end end -- Callback when updating function love.update(dt) -- On click: Pick nearest point to mouse position if love.mouse.isDown("l") then local mouseX, mouseY = love.mouse.getPosition() local closestPoint = nil local closestDistance = nil for _,p in ipairs(wavePoints) do local distance = math.abs(mouseX-p.x) if closestDistance == nil then closestPoint = p closestDistance = distance else if distance <= closestDistance then closestPoint = p closestDistance = distance end end end closestPoint.y = love.mouse.getY() end -- Update positions of points updateWavePoints(wavePoints, dt) end local circle = love.graphics.circle local line = love.graphics.line local color = love.graphics.setColor love.graphics.setBackgroundColor(0xff,0xff,0xff) -- Callback for drawing function love.draw(dt) -- Draw baseline color(0xff,0x33,0x33) line(0, Y_OFFSET, WIDTH, Y_OFFSET) -- Draw points and line color(0x00,0x33,0xbb) for n,p in ipairs(wavePoints) do circle("line", p.x, p.y, 3) if n == 1 then else local leftPoint = wavePoints[n-1] line(leftPoint.x, leftPoint.y, p.x, p.y) end end end [1]: https://love2d.org/ [2]: https://i.sstatic.net/EjOuh.png [3]: http://en.wikipedia.org/wiki/Hooke%27s_law