diff --git a/minetest.conf.example b/minetest.conf.example
index f5e4e85ddc747e83c94724d9d98234e24bc874ae..3f20eb7708f0441ae05352b9083fe024ae4d1ff7 100644
--- a/minetest.conf.example
+++ b/minetest.conf.example
@@ -24,6 +24,9 @@
 # 'permanent flame' nodes will remain with either setting.
 #enable_fire = true
 
+# Enable flame sound.
+#flame_sound = true
+
 # Whether the stuff in initial_stuff should be given to new players
 #give_initial_stuff = false
 #initial_stuff = default:pick_steel,default:axe_steel,default:shovel_steel,default:torch 99,default:cobble 99
diff --git a/mods/fire/README.txt b/mods/fire/README.txt
index 35affc264032080dfda818a8e83b00d4561a08b8..099da1c245dd84f298d08fe056b98b7abb03f690 100644
--- a/mods/fire/README.txt
+++ b/mods/fire/README.txt
@@ -12,17 +12,24 @@ Authors of media (textures and sounds)
 Everything not listed in here:
 Copyright (C) 2012 Perttu Ahola (celeron55) <celeron55@gmail.com> (CC BY-SA 3.0)
 
-fire_basic_flame_animated.png:
-  Muadtralk (CC BY-SA 3.0)
+Muadtralk (CC BY-SA 3.0)
+  fire_basic_flame_animated.png
 
-fire_flint_steel.png
-  Gambit (CC BY-SA 3.0)
+Gambit (CC BY-SA 3.0)
+  fire_flint_steel.png
 
-fire_small.ogg sampled from:
-  http://www.freesound.org/people/dobroide/sounds/4211/ (CC BY 3.0)
+dobroide (CC BY 3.0)
+http://www.freesound.org/people/dobroide/sounds/4211/
+  fire_small.ogg
 
-fire_large.ogg sampled from:
-  http://www.freesound.org/people/Dynamicell/sounds/17548/ (CC BY 3.0)
+Dynamicell (CC BY 3.0)
+http://www.freesound.org/people/Dynamicell/sounds/17548/
+  fire_large.ogg
+  fire_fire.*.ogg
 
-fire_flint_and_steel.ogg
-  https://www.freesound.org/people/Benboncan/sounds/66457/ (CC BY 3.0)
+fire_small.ogg and fire_large.ogg are unused but kept temporarily to not break
+other mods that may use them.
+
+Benboncan (CC BY 3.0)
+https://www.freesound.org/people/Benboncan/sounds/66457/
+  fire_flint_and_steel.ogg
diff --git a/mods/fire/init.lua b/mods/fire/init.lua
index 0dbd7c3e5e9654a3669b58b2e593c379b7107bcd..bee487a57e2d42c0bc264ebcd53cd44a50707963 100644
--- a/mods/fire/init.lua
+++ b/mods/fire/init.lua
@@ -1,11 +1,13 @@
--- minetest/fire/init.lua
-
 -- Global namespace for functions
 
 fire = {}
 
 
--- Register flame nodes
+--
+-- Items
+--
+
+-- Flame nodes
 
 minetest.register_node("fire:basic_flame", {
 	drawtype = "firelike",
@@ -34,22 +36,17 @@ minetest.register_node("fire:basic_flame", {
 			minetest.remove_node(pos)
 			return
 		end
-		-- restart timer
+		-- Restart timer
 		return true
 	end,
 	drop = "",
 
 	on_construct = function(pos)
 		minetest.get_node_timer(pos):start(math.random(30, 60))
-		minetest.after(0, fire.update_sounds_around, pos)
 	end,
 
-	on_destruct = function(pos)
-		minetest.after(0, fire.update_sounds_around, pos)
+	on_blast = function() -- Unaffected by explosions
 	end,
-
-	on_blast = function()
-	end, -- unaffected by explosions
 })
 
 minetest.register_node("fire:permanent_flame", {
@@ -76,7 +73,7 @@ minetest.register_node("fire:permanent_flame", {
 	groups = {igniter = 2, dig_immediate = 3},
 	drop = "",
 
-	on_blast = function()
+	on_blast = function() -- Unaffected by explosions
 	end,
 })
 
@@ -113,10 +110,10 @@ minetest.register_tool("fire:flint_and_steel", {
 			end
 		end
 		if not minetest.setting_getbool("creative_mode") then
-			-- wear tool
+			-- Wear tool
 			local wdef = itemstack:get_definition()
 			itemstack:add_wear(1000)
-			-- tool break sound
+			-- Tool break sound
 			if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
 				minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5})
 			end
@@ -151,72 +148,134 @@ minetest.override_item("default:coalblock", {
 	end,
 })
 
--- Get sound area of position
 
-fire.D = 6 -- size of sound areas
+--
+-- Sound
+--
 
-function fire.get_area_p0p1(pos)
-	local p0 = {
-		x = math.floor(pos.x / fire.D) * fire.D,
-		y = math.floor(pos.y / fire.D) * fire.D,
-		z = math.floor(pos.z / fire.D) * fire.D,
-	}
-	local p1 = {
-		x = p0.x + fire.D - 1,
-		y = p0.y + fire.D - 1,
-		z = p0.z + fire.D - 1
-	}
-	return p0, p1
+local flame_sound = minetest.setting_getbool("flame_sound")
+if flame_sound == nil then
+	-- Enable if no setting present
+	flame_sound = true
 end
 
+if flame_sound then
 
--- Fire sounds table
--- key: position hash of low corner of area
--- value: {handle=sound handle, name=sound name}
-fire.sounds = {}
+	local handles = {}
+	local timer = 0
 
+	-- Parameters
 
--- Update fire sounds in sound area of position
+	local radius = 8 -- Flame node search radius around player
+	local cycle = 3 -- Cycle time for sound updates
 
-function fire.update_sounds_around(pos)
-	local p0, p1 = fire.get_area_p0p1(pos)
-	local cp = {x = (p0.x + p1.x) / 2, y = (p0.y + p1.y) / 2, z = (p0.z + p1.z) / 2}
-	local flames_p = minetest.find_nodes_in_area(p0, p1, {"fire:basic_flame"})
-	--print("number of flames at "..minetest.pos_to_string(p0).."/"
-	--		..minetest.pos_to_string(p1)..": "..#flames_p)
-	local should_have_sound = (#flames_p > 0)
-	local wanted_sound = nil
-	if #flames_p >= 9 then
-		wanted_sound = {name = "fire_large", gain = 0.7}
-	elseif #flames_p > 0 then
-		wanted_sound = {name = "fire_small", gain = 0.9}
-	end
-	local p0_hash = minetest.hash_node_position(p0)
-	local sound = fire.sounds[p0_hash]
-	if not sound then
-		if should_have_sound then
-			fire.sounds[p0_hash] = {
-				handle = minetest.sound_play(wanted_sound,
-					{pos = cp, max_hear_distance = 16, loop = true}),
-				name = wanted_sound.name,
-			}
+	-- Update sound for player
+
+	function fire.update_player_sound(player)
+		local player_name = player:get_player_name()
+		-- Search for flame nodes in radius around player
+		local ppos = player:getpos()
+		local areamin = vector.subtract(ppos, radius)
+		local areamax = vector.add(ppos, radius)
+		local fpos, num = minetest.find_nodes_in_area(
+			areamin,
+			areamax,
+			{"fire:basic_flame", "fire:permanent_flame"}
+		)
+		-- Total number of flames in radius
+		local flames = (num["fire:basic_flame"] or 0) +
+			(num["fire:permanent_flame"] or 0)
+		-- Stop previous sound
+		if handles[player_name] then
+			minetest.sound_stop(handles[player_name])
+			handles[player_name] = nil
 		end
-	else
-		if not wanted_sound then
-			minetest.sound_stop(sound.handle)
-			fire.sounds[p0_hash] = nil
-		elseif sound.name ~= wanted_sound.name then
-			minetest.sound_stop(sound.handle)
-			fire.sounds[p0_hash] = {
-				handle = minetest.sound_play(wanted_sound,
-					{pos = cp, max_hear_distance = 16, loop = true}),
-				name = wanted_sound.name,
-			}
+		-- If flames
+		if flames > 0 then
+			-- Find centre of flame positions
+			local fposmid = fpos[1]
+			-- If more than 1 flame
+			if #fpos > 1 then
+				local fposmin = areamax
+				local fposmax = areamin
+				for i = 1, #fpos do
+					local fposi = fpos[i]
+					if fposi.x > fposmax.x then
+						fposmax.x = fposi.x
+					end
+					if fposi.y > fposmax.y then
+						fposmax.y = fposi.y
+					end
+					if fposi.z > fposmax.z then
+						fposmax.z = fposi.z
+					end
+					if fposi.x < fposmin.x then
+						fposmin.x = fposi.x
+					end
+					if fposi.y < fposmin.y then
+						fposmin.y = fposi.y
+					end
+					if fposi.z < fposmin.z then
+						fposmin.z = fposi.z
+					end
+				end
+				fposmid = vector.divide(vector.add(fposmin, fposmax), 2)
+			end
+			-- Play sound
+			local handle = minetest.sound_play(
+				"fire_fire",
+				{
+					pos = fposmid,
+					to_player = player_name,
+					gain = math.min(0.06 * (1 + flames * 0.125), 0.18),
+					max_hear_distance = 32,
+					loop = true, -- In case of lag
+				}
+			)
+			-- Store sound handle for this player
+			if handle then
+				handles[player_name] = handle
+			end
 		end
 	end
+
+	-- Cycle for updating players sounds
+
+	minetest.register_globalstep(function(dtime)
+		timer = timer + dtime
+		if timer < cycle then
+			return
+		end
+
+		timer = 0
+		local players = minetest.get_connected_players()
+		for n = 1, #players do
+			fire.update_player_sound(players[n])
+		end
+	end)
+
+	-- Stop sound and clear handle on player leave
+
+	minetest.register_on_leaveplayer(function(player)
+		local player_name = player:get_player_name()
+		if handles[player_name] then
+			minetest.sound_stop(handles[player_name])
+			handles[player_name] = nil
+		end
+	end)
 end
 
 
+-- Deprecated function kept temporarily to avoid crashes if mod fire nodes call it
+
+function fire.update_sounds_around(pos)
+end
+
+
+--
+-- ABMs
+--
+
 -- Extinguish all flames quickly with water, snow, ice
 
 minetest.register_abm({
@@ -229,7 +288,7 @@ minetest.register_abm({
 	action = function(pos, node, active_object_count, active_object_count_wider)
 		minetest.remove_node(pos)
 		minetest.sound_play("fire_extinguish_flame",
-			{pos = pos, max_hear_distance = 16, gain = 0.25})
+			{pos = pos, max_hear_distance = 16, gain = 0.15})
 	end,
 })
 
@@ -245,7 +304,7 @@ end
 
 if not fire_enabled then
 
-	-- Remove basic flames only
+	-- Remove basic flames only if fire disabled
 
 	minetest.register_abm({
 		label = "Remove disabled fire",
@@ -279,7 +338,7 @@ else -- Fire enabled
 		end,
 	})
 
-	-- Remove flammable nodes
+	-- Remove flammable nodes around basic flame
 
 	minetest.register_abm({
 		label = "Remove flammable nodes",
@@ -291,7 +350,6 @@ else -- Fire enabled
 		action = function(pos, node, active_object_count, active_object_count_wider)
 			local p = minetest.find_node_near(pos, 1, {"group:flammable"})
 			if p then
-				-- remove flammable nodes around flame
 				local flammable_node = minetest.get_node(p)
 				local def = minetest.registered_nodes[flammable_node.name]
 				if def.on_burn then
@@ -305,35 +363,3 @@ else -- Fire enabled
 	})
 
 end
-
-
--- Rarely ignite things from far
-
---[[ Currently disabled to reduce the chance of uncontrollable spreading
-	fires that disrupt servers. Also for less lua processing load.
-
-minetest.register_abm({
-	nodenames = {"group:igniter"},
-	neighbors = {"air"},
-	interval = 5,
-	chance = 10,
-	action = function(pos, node, active_object_count, active_object_count_wider)
-		local reg = minetest.registered_nodes[node.name]
-		if not reg or not reg.groups.igniter or reg.groups.igniter < 2 then
-			return
-		end
-		local d = reg.groups.igniter
-		local p = minetest.find_node_near(pos, d, {"group:flammable"})
-		if p then
-			-- If there is water or stuff like that around flame, don't ignite
-			if fire.flame_should_extinguish(p) then
-				return
-			end
-			local p2 = fire.find_pos_for_flame_around(p)
-			if p2 then
-				minetest.set_node(p2, {name = "fire:basic_flame"})
-			end
-		end
-	end,
-})
---]]
diff --git a/mods/fire/sounds/fire_fire.1.ogg b/mods/fire/sounds/fire_fire.1.ogg
new file mode 100644
index 0000000000000000000000000000000000000000..cbfee4c65f4739acc951e8f87f30d824f1ae21ea
Binary files /dev/null and b/mods/fire/sounds/fire_fire.1.ogg differ
diff --git a/mods/fire/sounds/fire_fire.2.ogg b/mods/fire/sounds/fire_fire.2.ogg
new file mode 100644
index 0000000000000000000000000000000000000000..e8d0eb1356c3ef33949dae57847c88988a1c38a9
Binary files /dev/null and b/mods/fire/sounds/fire_fire.2.ogg differ
diff --git a/mods/fire/sounds/fire_fire.3.ogg b/mods/fire/sounds/fire_fire.3.ogg
new file mode 100644
index 0000000000000000000000000000000000000000..5cad3d9b8a9ffb1f6e41e950a1bd4997d88cb427
Binary files /dev/null and b/mods/fire/sounds/fire_fire.3.ogg differ
diff --git a/settingtypes.txt b/settingtypes.txt
index 2f91a7b6bdbf5ba0558eea4b22fd53f5643b98c1..eeea0bfcdb8a3ab7799498ce735141e355755adb 100644
--- a/settingtypes.txt
+++ b/settingtypes.txt
@@ -13,6 +13,9 @@ creative_mode (Creative mode) bool false
 #    'permanent_flame' nodes are unaffected.
 enable_fire (Fire) bool true
 
+#    Enable flame sound.
+flame_sound (Flame sound) bool true
+
 #    If enabled, steel tools, torches and cobblestone will be given to new
 #    players.
 give_initial_stuff (Give initial items) bool false