Skip to main content

Creating a Liquid Simulation in Blender Using Python

About Us
Published by JET BI
24 December 2024
139

How to create a liquid simulation in Blender with Python

Introduction

Fluid graphics are crucial in realistic 3D animations, and Blender, with its built-in
Python interface (BPY), provides the means to streamline and adapt fluid graphics.
You'll know the simple ideas about water moving in simulations and how to write
the code to do it faster by reading this piece.

 

What is Blender?

Blender is a no-cost, publicly accessible 3D software package featuring extensive
utilities for functions such as the creation of models, motion sequences, physical
effects, image synthesis, and motion picture crafting techniques. It's favoured across
both professionals and enthusiasts for crafting animated films, visual effects, 3D
components, and interactive programs.

Blender provides two primary ways to interact with its features.
A window displays tools & actions visually. 

Automation interface - Work process customisation- 3D object creation


(procedural)

 

What is BPY?

Blender's BPY lets users add special commands to manage Blender easily. Here's
what you can do with it.

Construct shapes, light sources, view devices, and extra features with code.
Do repetitive work faster by automating.
Adjust liquid or motion effects and enhance colour effects without touching the
main panel.

Introduce new tools, features, or custom interfaces to improve suitability.
BPY connects imagination in 3D art with the accuracy of coding. It’s an essential aid
for creators and designers to fully explore Blender’s features.

This article will cover the following steps.

  • Setting up the fluid domain.
  • Adding a fluid source.
  • Applying transformations.
  • Adding realistic water material.
  • Baking the simulation.
  • Denoising and rendering.

 

Step 1: Initial Scene Setup in Blender

At this point, a new scene is opened in Blender. By default, the following objects are
already present in a new scene.
Spot in the middle (Box thing) is at 0, 0, 0. This object is often used as a base for
further modeling. In the future, it will be transformed into a section for flow
depictions.
Tilted slightly to see cubes when drawing the view.
Light: A point of light needed to illuminate objects in the scene.


Step 2: Splitting the Screen in Blender


Now you've divided the screen space in Blender into two parts. This facilitates
simultaneous multitasking, enabling activities like coding on one monitor and 3D
rendering on another.
How to split the screen in Blender.
Slide your click-point over the dividing line till it shows the divide symbol.
Right-click and select “Split Area”.
Moving the boundary line where you want and releasing it allows you to secure the
division fixed in place.


Step 3: Opening a Text Editor for Scripting.


Next, you've selected one of the window sections to utilize Blender's text space. This
allows you to write and run Python scripts directly in Blender.
How to open a text editor in Blender.
Selecting a window: Select one of the split areas of the screen.
Push the button in the top left corner and select "Words in Paper" from the choices
provided. This will switch the selected window to text editor mode.
Now you can write or paste Python scripts for running jobs automatically.


Step 4: Make a fresh text file in Blender's text editor


In this step, we create a new text file in Blender's text tool for writing a Python
script.
Steps to create a new text document.
Go to a text-write window. Ensure one of the windows is configured to text editor
operational setting from the preceding action.
Make a clean file and click the "New" button at the top right corner of the text
editor. This switch generates a fresh document, allowing one to create code scripts.

 

Step 5: Import the BPY library


At this point, the first line of code is added to the textual content editor — import
BPY. This is a preferred step to get commenced with Python in Blender, permitting
you to get right off entry to the Blender API.

import bpy

 

Step 6: Writing the first a part of the code to create the area

In this step, the script to create the fluid simulation domain is written in a textual content editor. This code converts the item (cube) into a domain that defines the bounds within which the simulation will take place.

import bpy
# Create the domain
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
domain = bpy.context.object
domain.name = "Domain"
bpy.ops.object.modifier_add(type='FLUID')
domain.modifiers["Fluid"].fluid_type = 'DOMAIN'
domain.modifiers["Fluid"].domain_settings.domain_type = 'LIQUID'
domain.modifiers["Fluid"].domain_settings.resolution_max = 32
domain.modifiers["Fluid"].domain_settings.cache_directory = "//cache_fluid"

Creating a domain object:

bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0)) 

Creates a cube in the center of the scene (coordinates 0, 0, 0) with a size of 2 Blender units.

This cube will act as a domain, which will enclose the area of ​​the fluid simulation.

Renaming a domain object:

domain.name = "Domain"

Gives the cube the name "Domain", which makes it easier to work with.

Adding a fluid modifier:

bpy.ops.object.modifier_add(type='FLUID')

Adds a fluid simulation modifier to the object.

domain.modifiers["Fluid"].fluid_type = 'DOMAIN'

Specifies that the object will be used as a simulation domain.

Setting up domain parameters:

domain.modifiers["Fluid"].domain_settings.domain_type = 'LIQUID' 

Sets the domain type for working with liquid.

domain.modifiers["Fluid"].domain_settings.resolution_max = 32

Determines the quality of the simulation. The higher the value, the more details, but it increases the calculation time.

domain.modifiers["Fluid"].domain_settings.cache_directory = "//cache_fluid" 

Specifies the folder for saving the simulation data. The prefix "//" denotes the relative path to the project file.

Helpful tips:

Resolution (resolution_max):

For tests, a value of 32 is sufficient.

For final rendering, it is recommended to use 64 or higher.

Caching: Setting the cache path is important for reusing the simulation.

Here is the result of this script if we Press Alt+P
 


 

After executing the primary part of the code, an object appears in the scene, converted to a fluid simulation area. In the photo, you could see that the object (dice) now has the name "Domain" defined in the script.

What happened after executing the code:

An area object is created: The cube turned into added to the scene the usage of “bpy.Ops.Mesh.Primitive_cube_add()” and converted to a website with the "Fluid" modifier.

Domain type is configured: The domain kind is ready to LIQUID, which makes the object ready for fluid simulation.

Caching: The area settings are configured to shop the simulation information in the //cache_fluid folder.

 

Step 7: Writing the second one part of the code to feature the fluid source

At this factor, the code that creates the fluid source, so one can generate fluid inside the domain, is introduced within the textual content editor.

# Create the fluid source
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.2, location=(0, 0, 1))
source = bpy.context.object
source.name = "Source"
bpy.ops.object.modifier_add(type='FLUID')
source.modifiers["Fluid"].fluid_type = 'FLOW'
source.modifiers["Fluid"].flow_settings.flow_type = 'LIQUID'
source.modifiers["Fluid"].flow_settings.flow_behavior = 'INFLOW'


Creating a source object:

bpy.ops.mesh.primitive_uv_sphere_add(radius=0.2, location=(0, 0, 1))


Creates a sphere with a radius of 0.2 Blender units at coordinates (0, 0, 1).

This sphere will emit fluid into the domain.

Renaming the source object:

source.name = "Source"

Sets the object's name to "Source".

Adding a fluid modifier:

bpy.ops.object.modifier_add(type='FLUID')


Adds a fluid simulation modifier.

source.modifiers["Fluid"].fluid_type = 'FLOW'

Specifies that the object will be used as a fluid source.

Setting the source parameters:

source.modifiers["Fluid"].flow_settings.flow_type =

Sets the emitted material type to liquid.

source.modifiers["Fluid"].flow_settings.flow_behavior = 'INFLOW'

Specifies continuous emission of fluid.

Helpful tips:

The source can be used to create effects such as water jets, pouring, or dripping.

The flow_behavior parameter can be used to set the source to emit a single burst (OUTFLOW) or a continuous flow (INFLOW).

Result of the second part of the code: including a fluid source

After executing the code, a new item seemed in the scene - a sphere located above the domain. This object is ready as a fluid source, so one can generate a float of fluid into the area.


 

What happened within the scene:

A fluid source became created:

A sphere placed at coordinates (0, 0, 1) became delivered the use of the “bpy.Ops.Mesh.Primitive_uv_sphere_add()” command and named "Source".

This item is ready as a fluid source way to the "Fluid" modifier with the Flow kind.

Continuous fluid generation:

The supply is about to INFLOW, which means continuous emission of fluid into the area.

Object interactions:

The sphere is inside the area, which allows the simulation to efficaciously deal with the physical interactions of the fluid.

 

Step 8: Applying Transforms to Objects

At this point, we've introduced code to use transforms to all objects in the scene. This is an essential step to make certain the simulation works efficiently.

# Apply transformations
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

Selecting all objects:

bpy.ops.object.select_all(action='SELECT')

Selects all objects in the scene.
Applying transforms:

bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

"Bake" the current transforms of objects (position, rotation, scale) into their base properties.

Why is so vital?

Blender uses transformation matrices to determine the size, position, and orientation of objects. Applying transforms ensures that the simulation interprets these values ​​correctly.

 

Step 9: Create and assign a sensible water fabric

In this step, code is delivered to create a material on the way to simulate the houses of water. This material includes color and refraction settings appropriate for realistically rendering liquid.

# Create a new material
material = bpy.data.materials.new(name="RealisticWaterMaterial")
material.use_nodes = True
nodes = material.node_tree.nodes

# Clear defualt nodes
for node in nodes:
    nodes.remove(node)

# Add new nodes
output_node = nodes.new(type='ShaderNodeOutputMaterial')
output_node.location = (300, 0)

glass_bsdf = nodes.new(type='ShaderNodeBsdfGlass')
glass_bsdf.location = (0, 0)

# Set water properties
glass_bsdf.inputs['Color'].default_value = (0.2, 0.5, 0.8, 1.0)  оттенок
glass_bsdf.inputs['IOR'].default_value = 1.33 

#Connect nodes
material.node_tree.links.new(glass_bsdf.outputs['BSDF'], output_node.inputs['Surface'])

# Assign the material to the domain
domain.data.materials.append(material)


Creating a new material:

bpy.data.materials.new(name="RealisticWaterMaterial")

Creates a new material named "RealisticWaterMaterial".

material.use_nodes = True

Enables nodes for the material.
 

Cleaning up default nodes:

Removing default material nodes to create a custom shader network.
 

Adding nodes:

The "ShaderNodeBsdfGlass" node simulates transparency and refraction of water.

The index of refraction (IOR) is set to 1.33 to realistically display water.
 

Assigning a material:

The material is assigned to the fluid domain for rendering.
 

Helpful tips:

The color can be adjusted to create the effect of colored water.

Add textures or noise for more realism.



10 Step: Baking the Fluid Simulation

This step provides the simulation baking code, which creates the very last records to show the fluid movement. Baking the simulation allows you to visualize how the fluid will interact with the area and source.

bpy.context.view_layer.objects.active = domain # Set domain as active object
bpy.ops.fluid.bake_all()


Setting the active object:

bpy.context.view_layer.objects.active = domain

Makes the domain object active, which is necessary for baking the simulation.

Bake the simulation:

bpy.ops.fluid.bake_all()

Starts the process of baking all the simulation data.
 

Helpful tips:

Baking may take some time depending on the complexity of the simulation.

Make sure the cache path is set up correctly to save the data.

Final end result of jogging the code: a custom-designed fluid simulation scene

The image indicates the final end result of strolling the code. The scene incorporates the subsequent elements:

Domain: A large dice representing the place wherein the fluid simulation occurs.

Source: A small sphere placed above the area that generates the fluid.

Now we are going to switch the display mode in Material Preview to see the color and material assigned to objects.

The domain, which turned into formerly a regular cube, now seems as a container with water cloth, having a sensible blue-green tint and reflections.


 

Step 11: Running the Fluid Simulation Animation

At this factor, you are prepared to play the fluid simulation animation in Blender to peer how the fluid is generated with the aid of the source and fills the domain.

This is the result we should get  (gif)

This photograph shows the key objects in the scene and visualizes the first level of the fluid simulation. We can see the interaction between the source and the domain, creating a fluid flow internal a restrained space.

In the animation, we see that the liquid is broken into many fragments, and it is not as smooth and flexible as real water. To fix this, we should add antialiasing to the domain geometry to make the fluid animation smoother and more realistic. To do this, use the Shade Auto Smooth function. 


This is the result we should get (gif)

As you can see in the gif, there are colored pixels in the animation - they are called noise.

Fortunately, Blender has a lot of tools to help us remove them.

First of all, we go into the render section, and change the EEVEE to cycles.

Cycles presents ray tracing, which makes reflections, refractions, and shadows a lot higher.


Step 12: Setting up Denoising for Cycles

In this step, we permit Denoising to take away noise from the final render. This feature greatly improves photographing first-class without having to boom the number of samples to excessive values.

Next, we have to enable Denoising Data within the View Layer settings to skip extra information for denoising in publish-processing. This is useful while the usage of Cycles renders to make the end result look cleaner and extra super.

 

And now the final and most important touch

At this point, you have moved directly to the Compositing section to refine the final render, in the use of the node system. This lets you use additional outcomes, consisting of noise reduction, progressed color correction, and put-up-processing.

Add a Denoise node to the compositing to remove noise in the final render.

We connect the nodes as shown in the picture


To visualize the rendered result without delay within the Blender workspace we upload a Viewer node.

At this stage, the nodes inside the composition editor are linked in this sort of way as to make certain correct noise discount, visualization of intermediate consequences, and final output.

After rendering, all 250 frames will be saved in this folder; 

You can rename it as you wish.

The very last step in growing an animation is rendering all the frames and saving them. At this level, you begin the system of rendering the entire scene, contemplating all the formerly set parameters. 

Since there are a lot of frames, the rendering process can take quite a while.

Be patient - the result is worth it!!!

In the directory you specified before rendering, you can see all the rendered frames of your animation with denoising applied. These frames can now be used to create video.

In Blender's video sequencer, you can gather all rendered frames right into a finished animation.

At this factor, you pick out the Image/Sequence option to upload your rendered pictures to Blender's sequencer.

Next, you've got selected the rendered images within the tmp folder and clicked the Add Image Strip button. Your photos will now be introduced as a chain of frames to the sequencer timeline.

To do this, you need to

Press Shift,   A → Image/Sequence.

Select all 250 frames of the render.

Now, you have got successfully delivered a rendered photograph collection to the Video Sequencer.

There's only a little bit left...

Installation of the render parameters to create the video and setting the output directory.

As proven in the screenshot - this could provide us with a more pleasant video.

Press Ctrl + F12 to export video.
Animation creation process!


Congratulations on efficiently growing an animation!

Now you could enjoy your video determined within the specific directory.

 

The Final Result!

import bpy

# Create the domain
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
domain = bpy.context.object
domain.name = "Domain"
bpy.ops.object.modifier_add(type='FLUID')
domain.modifiers["Fluid"].fluid_type = 'DOMAIN'
domain.modifiers["Fluid"].domain_settings.domain_type = 'LIQUID'
domain.modifiers["Fluid"].domain_settings.resolution_max = 32
domain.modifiers["Fluid"].domain_settings.cache_directory = "//cache_fluid"

# Create the fluid source
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.2, location=(0, 0, 1))
source = bpy.context.object
source.name = "Source"
bpy.ops.object.modifier_add(type='FLUID')
source.modifiers["Fluid"].fluid_type = 'FLOW'
source.modifiers["Fluid"].flow_settings.flow_type = 'LIQUID'
source.modifiers["Fluid"].flow_settings.flow_behavior = 'INFLOW'

# Apply transformations
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

# Create a new material
material = bpy.data.materials.new(name="RealisticWaterMaterial")
material.use_nodes = True
nodes = material.node_tree.nodes

# Clear defualt nodes
for node in nodes:
    nodes.remove(node)

# Add new nodes
output_node = nodes.new(type='ShaderNodeOutputMaterial')
output_node.location = (300, 0)

glass_bsdf = nodes.new(type='ShaderNodeBsdfGlass')
glass_bsdf.location = (0, 0)


# Set water properties
glass_bsdf.inputs['Color'].default_value = (0.2, 0.5, 0.8, 1.0)  
glass_bsdf.inputs['IOR'].default_value = 1.33  

#Connece nodes
material.node_tree.links.new(glass_bsdf.outputs['BSDF'], output_node.inputs['Surface'])

# Assign the material to the domain
domain.data.materials.append(material)

bpy.context.view_layer.objects.active = domain # Set domain as active object
bpy.ops.fluid.bake_all()

 

Conclusion

Blender combined with Python is a powerful tool that allows you to create complex simulations and automate labor-intensive processes. In the example provided, you can develop your programming skills and work on more complex projects, including multi-layer modeling or integration with other 3D tools.


Roman Smyk
Python Developer/Data Analyst
image
Expertise
Question to the expert
image

We have available resources to start working on your project within 5 business days

1 UX Designer

image

1 Admin

image

2 QA engineers

image

1 Consultant

image
Related Articles
All articles
image
Is Salesforce Winning the Public Sector Race?
An analysis of Salesforce's rapid expansion into the U.S. public sector, tracing its path from cautious early government licensing deals in the 2010s through the launch of Government Cloud in 2012, its pivotal role in COVID-19 vaccine rollouts, and its 2025–2026 push into military and intelligence work via Agentforce and Missionforce. The piece covers major 2026 contracts — including a $5.6 billion Army deal, a $1.6 billion VA agreement, and Pentagon Impact Level 5 authorization — alongside real-world case studies like California's REAL ID processing and the UK's NHS back-office operations. It also examines the structural obstacles still facing Salesforce and other vendors in government tech: legacy IT systems decades old, outdated federal procurement rules, budget constraints, and organizational caution around AI adoption, plus the competitive pressure from Palantir, Microsoft, and Oracle in the race for public sector AI spending.
28 August 2026
image
Why Your Salesforce Flows Are Agentforce's Biggest Problem
This article argues that the most underestimated risk in Agentforce deployments isn't data quality — it's the automation layer: years of overlapping Flows, Process Builder processes, Apex triggers, and managed package logic that no one has reviewed end-to-end. It explains why AI agents inherit automation complexity without the tribal knowledge human admins carry, why technical debt only becomes visible after an agent hits it in production, and why a clean demo is no indicator of production readiness. The article closes with a concrete, tool-by-tool inventory approach using Flow Trigger Explorer, Salesforce Optimizer, Setup Audit Trail, Apex Debug Logs, Agent Builder, and Health Check — scoped to the specific processes the agent will actually use rather than the whole org.
23 July 2026
image
How to Wire Multiple Salesforce Projects in One Org Without Breaking Everything
This article maps the real integration patterns that emerge when multiple Salesforce projects — both managed packages and unpackaged code — share a single org. It covers four concrete patterns: attaching custom triggers to package-owned objects, calling global members exposed by managed packages, writing directly into another project's objects, and runtime-guarded reads of package data. It then addresses access control for authenticated and guest users, including the Master-Detail wall and the without sharing elevation pattern. The piece closes with eight concrete risks (compile-time dependencies that block uninstall, upgrade coupling, silent cascade failures, access invisible to admins) and six actionable recommendations for keeping cross-project coupling manageable.
08 July 2026