diff --git a/.gitattributes b/.gitattributes
index 2d2dda21c8..529481c82b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -19000,6 +19000,8 @@ utils/wasmbin/testscan/inst_const_i64_neg_hex.wat svneol=native#text/plain
 utils/wasmbin/testscan/inst_const_i64_pos.wat svneol=native#text/plain
 utils/wasmbin/testscan/inst_if_value.wat svneol=native#text/plain
 utils/wasmbin/testscan/inst_unreachable.wat svneol=native#text/plain
+utils/wasmbin/wasa.lpi svneol=native#text/plain
+utils/wasmbin/wasa.pas svneol=native#text/plain
 utils/wasmbin/wasmbin.pas svneol=native#text/plain
 utils/wasmbin/wasmbincode.pas svneol=native#text/plain
 utils/wasmbin/wasmbindebug.pas svneol=native#text/plain
diff --git a/utils/wasmbin/wasa.lpi b/utils/wasmbin/wasa.lpi
new file mode 100644
index 0000000000..2ad97f5cd6
--- /dev/null
+++ b/utils/wasmbin/wasa.lpi
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <Title Value="wasa"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes Count="1">
+      <Item1 Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+      <Modes Count="0"/>
+    </RunParams>
+    <Units Count="2">
+      <Unit0>
+        <Filename Value="wasa.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="wasmbinwriter.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit1>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <Target>
+      <Filename Value="wasa"/>
+    </Target>
+    <SearchPaths>
+      <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
+</CONFIG>
diff --git a/utils/wasmbin/wasa.pas b/utils/wasmbin/wasa.pas
new file mode 100644
index 0000000000..b83342d24c
--- /dev/null
+++ b/utils/wasmbin/wasa.pas
@@ -0,0 +1,121 @@
+program wasa;
+
+{$mode objfpc}{$H+}
+
+uses
+  SysUtils, Classes, watparser, watscanner, wasmmodule, wasmbinwriter,
+  wasmnormalize;
+
+procedure Traverse(p: TWatScanner);
+begin
+  while p.Next do begin
+    write(p.token,' ', p.resText);
+    if p.token = weInstr then
+      write('; inst = $', IntToHex(p.instrCode,2))
+    else if p.token = weError then begin
+      writeln('offset = ',p.ofs,' ',p.resText);
+      break;
+    end;
+    writeln;
+  end;
+end;
+
+procedure WriteBin(const fndst: string; m: TWasmModule; WriteReloc: Boolean);
+var
+  f : TFileStream;
+begin
+  f := TFileStream.Create(fndst, fmCreate);
+  try
+    Normalize(m);
+    WriteModule(m, f, WriteReloc, WriteReloc);
+  finally
+    f.Free;
+  end;
+end;
+
+procedure Run(const fn: string; const doTraverse: Boolean; doReloc: Boolean);
+var
+  st : TFileStream;
+  s  : string;
+  p  : TWatScanner;
+  m  : TWasmModule;
+  err : string;
+begin
+  st := TFileStream.Create(fn, fmOpenRead or fmShareDenyNone);
+  p := TWatScanner.Create;
+  try
+    SetLength(s, st.Size);
+    if length(s)>0 then st.Read(s[1], length(s));
+    p.SetSource(s);
+    if doTraverse then begin
+      Traverse(p);
+      Exit;
+    end;
+    m := TWasmModule.Create;
+    try
+      if not ParseModule(p, m, err) then
+        writeln('Error: ', err)
+      else
+        WriteBin( ChangeFileExt(fn,'.wasm'), m, doReloc);
+    finally
+      m.Free;
+    end;
+  finally
+    p.Free;
+    st.Free;
+  end;
+end;
+
+var
+  gFn      : string;
+  gCommand : string = '';
+  gReloc   : Boolean = true;
+  gCatch   : Boolean = false;
+
+procedure ParseParams;
+var
+  i : integer;
+  s : string;
+  ls : string;
+begin
+  i:=1;
+  while i<=ParamCount do begin
+    s := ParamStr(i);
+    if (s<>'') and (s[1]='-') then begin
+      ls := AnsiLowerCase(s);
+      if ls = '-noreloc' then gReloc := false
+      else if ls = '-catch' then gCatch := true
+      else gCommand:=ls;
+    end else
+      gFn := s;
+    inc(i);
+  end;
+end;
+
+begin
+  ParseParams;
+  if (gFn='') then begin
+    writeln('please sepcify the input .wat file');
+    writeln('other use:');
+    writeln(' -compile  %inpfn%');
+    writeln('   -noreloc - prevents relocation information from being written');
+    writeln(' -traverse %inpfn%');
+    exit;
+  end;
+  if not FileExists(gFn) then begin
+    writeln('file doesn''t exist: ', gFn);
+    exit;
+  end;
+
+  if gCatch then
+    try
+      Run(gFn, gCommand = '-traverse', gReloc);
+    except
+      on e: exception do
+        writeln(e.message);
+    end
+  else
+    Run(gFn, gCommand = '-traverse', gReloc);
+
+end.
+