diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 84080502f0052e99a9e771923e4408c5ecb51206..80e66020dfaf7bf97f9bcd320cdb6391ff572a9e 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -4152,7 +4152,8 @@ The Biome API is still in an experimental phase and subject to change.
     --  ^ collision_removal: if true then particle is removed when it collides,
     --  ^ requires collisiondetection = true to have any effect
         attached = ObjectRef,
-    --  ^ attached: if defined, makes particle positions relative to this object.
+    --  ^ attached: if defined, particle positions, velocities and accelerations
+    --  ^ are relative to this object's position and yaw.
         vertical = false,
     --  ^ vertical: if true faces player using y axis only
         texture = "image.png",
diff --git a/src/clientobject.h b/src/clientobject.h
index c4e1a634b056b68d7a5f44faafb77995fcc3b821..83931e438e63194a3981514d6f1d41897bd4bfb8 100644
--- a/src/clientobject.h
+++ b/src/clientobject.h
@@ -61,6 +61,7 @@ class ClientActiveObject : public ActiveObject
 	virtual bool getCollisionBox(aabb3f *toset){return false;}
 	virtual bool collideWithObjects(){return false;}
 	virtual v3f getPosition(){return v3f(0,0,0);}
+	virtual float getYaw() const {return 0;}
 	virtual scene::ISceneNode *getSceneNode(){return NULL;}
 	virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;}
 	virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;}
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 88ed43a8c8a37697997f74af3f75a47dd85b7211..6b35d5881da39eddb77700c11b0c11549855a898 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -313,7 +313,8 @@ class ItemCAO : public ClientActiveObject
 		{return &m_selection_box;}
 	v3f getPosition()
 		{return m_position;}
-
+	inline float getYaw() const
+		{return 0;}
 	std::string infoText()
 		{return m_infotext;}
 
diff --git a/src/content_cao.h b/src/content_cao.h
index 5b34718140c7512915a90c7d8d3ee554aff96c7b..a158e8296761a61e957fb3051feff933c8422415 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -136,6 +136,10 @@ class GenericCAO : public ClientActiveObject
 	aabb3f *getSelectionBox();
 
 	v3f getPosition();
+	inline float getYaw() const
+	{
+		return m_yaw;
+	}
 
 	scene::ISceneNode *getSceneNode();
 
diff --git a/src/particles.cpp b/src/particles.cpp
index f20fb4083033631a48370ace89c6fc34a57be062..acf9cc8155b22dc2bf6f1e6238092f71b23a0226 100644
--- a/src/particles.cpp
+++ b/src/particles.cpp
@@ -253,12 +253,17 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 	m_time += dtime;
 
 	bool unloaded = false;
-	v3f attached_offset = v3f(0,0,0);
+	bool is_attached = false;
+	v3f attached_pos = v3f(0,0,0);
+	float attached_yaw = 0;
 	if (m_attached_id != 0) {
-		if (ClientActiveObject *attached = env->getActiveObject(m_attached_id))
-			attached_offset = attached->getPosition() / BS;
-		else
+		if (ClientActiveObject *attached = env->getActiveObject(m_attached_id)) {
+			attached_pos = attached->getPosition() / BS;
+			attached_yaw = attached->getYaw();
+			is_attached = true;
+		} else {
 			unloaded = true;
+		}
 	}
 
 	if (m_spawntime != 0) // Spawner exists for a predefined timespan
@@ -277,8 +282,15 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 					v3f pos = random_v3f(m_minpos, m_maxpos);
 					v3f vel = random_v3f(m_minvel, m_maxvel);
 					v3f acc = random_v3f(m_minacc, m_maxacc);
-					// Make relative to offest
-					pos += attached_offset;
+
+					if (is_attached) {
+						// Apply attachment yaw and position
+						pos.rotateXZBy(attached_yaw);
+						pos += attached_pos;
+						vel.rotateXZBy(attached_yaw);
+						acc.rotateXZBy(attached_yaw);
+					}
+
 					float exptime = rand()/(float)RAND_MAX
 							*(m_maxexptime-m_minexptime)
 							+m_minexptime;
@@ -321,10 +333,18 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 		{
 			if (rand()/(float)RAND_MAX < dtime)
 			{
-				v3f pos = random_v3f(m_minpos, m_maxpos)
-						+ attached_offset;
+				v3f pos = random_v3f(m_minpos, m_maxpos);
 				v3f vel = random_v3f(m_minvel, m_maxvel);
 				v3f acc = random_v3f(m_minacc, m_maxacc);
+
+				if (is_attached) {
+					// Apply attachment yaw and position
+					pos.rotateXZBy(attached_yaw);
+					pos += attached_pos;
+					vel.rotateXZBy(attached_yaw);
+					acc.rotateXZBy(attached_yaw);
+				}
+
 				float exptime = rand()/(float)RAND_MAX
 						*(m_maxexptime-m_minexptime)
 						+m_minexptime;